ExerciseChallenge
Challenge
Size six services
25 minintermediate1–15 yrs
Edge cases. You have to reason, and two valid fixes differ.
What this teaches
- The sizing question is unanswerable without hold time
- Two of the six should not be sized at all until something else is fixed
- Total connections across instances is the number the database sees
- A batch job and a request handler should not share a pool
Starter
Starter.javaOpen in playground
import java.util.*;
/**
* Challenge: six services, one database. Size each pool.
*
* Two of them cannot be sized sensibly until something else changes, and
* saying so is the correct answer rather than a dodge.
*/
public class Starter {
// ── 1 ─────────────────────────────────────────────────────────────────
// Product search. 2,000 req/s. One indexed query, p99 4 ms. 6 instances.
// ── 2 ─────────────────────────────────────────────────────────────────
// Checkout. 50 req/s. One transaction that also calls a payment gateway
// with a p99 of 900 ms, inside the transaction. 4 instances.
// ── 3 ─────────────────────────────────────────────────────────────────
// Nightly reconciliation. One instance, runs for 40 minutes, streams
// 2 million rows through a single connection.
// ── 4 ─────────────────────────────────────────────────────────────────
// Notification worker. 500 messages/s. Two queries per message, 3 ms
// each. Autoscales between 2 and 20 instances.
// ── 5 ─────────────────────────────────────────────────────────────────
// Admin dashboard. 1 req/s. Aggregate queries with a p99 of 6 seconds.
// 1 instance. Shares its pool with an hourly export job.
// ── 6 ─────────────────────────────────────────────────────────────────
// Public API on Java 21 with virtual threads. 3,000 req/s, one query at
// 5 ms. 8 instances. No thread pool bounding anything.
// The database: PostgreSQL, 8 cores, SSD, max_connections = 100.
public static void main(String[] args) {
// TODO 1: for each service, compute connections = rate x hold time.
// Show the arithmetic. Note where hold time is not the query time.
// TODO 2: two of the six produce a number the database cannot give.
// Name them and say what has to change before sizing means anything.
// TODO 3: service 3 needs one connection and holds it for 40 minutes.
// Is that a problem? Say what it does to max_lifetime and to the
// database's ability to clean up old row versions.
// TODO 4: service 4 autoscales. Give the per-instance pool and then
// the total at minimum and maximum scale. Which one breaks?
// TODO 5: service 5 shares a pool between a 6-second dashboard query
// and an export job. Say what the export does to the dashboard, then
// split them and give both sizes.
// TODO 6: service 6 has virtual threads and no thread pool. Say what
// used to limit database concurrency, what limits it now, and what
// that means for the acquisition timeout specifically.
// TODO 7: add up every connection across all six at full scale, plus
// monitoring and replication. Compare with max_connections = 100.
// If you are over, say what you would do — there are three options
// and they are not equally good.
// TODO 8: give an acquisition timeout for each service. They should
// not all be the same number, and the reason is about what the caller
// does when refused.
}
}Run it locally:
cd exercises/java/db-performance/connection-pool-sizing/02-challenge
javac Starter.java -d /tmp/out && java -cp /tmp/out StarterHints
Hint 1
For each service, write connections = rate x hold time before anything else. Two of them produce an absurd number — that is the answer.
Hint 2
One service's hold time is dominated by something that is not a query. Sizing it is the wrong task.
Hint 3
One is fine per instance and not fine in aggregate. Multiply.
Hint 4
One shares a pool between two workloads with very different hold times. Say what the slow one does to the fast one.
Done when
- A pool size, or a refusal to size, for each of the six with the arithmetic shown
- The two hold-time problems identified as hold-time problems
- A total connection count across all instances and tools, compared against max_connections
- The shared-pool case split, with a reason
- For each, the acquisition timeout you would set and why