ExerciseWarm-up
Warm-up
Exhaust a pool on purpose
10 minjunior0–15 yrs
One concept, guided. Near-impossible to fail.
What this teaches
- Connections needed is arrival rate times hold time, and nothing else
- Hold time decides everything, and it covers more than query execution
- Exhaustion fails at acquisition, before the database is involved
- A remote call inside the connection's lifetime is how pools actually die
Starter
Starter.javaOpen in playground
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
/**
* Warm-up: make a pool run out, then work out why.
*
* A Semaphore is a connection pool with the interesting parts kept and the
* JDBC removed. Every thread here must be a daemon, so a stuck request cannot
* hold the JVM open.
*/
public class Starter {
public static void main(String[] args) throws Exception {
// TODO 1: Little's Law first, on paper. A service takes 1000 requests
// per second and each holds a connection for 5 ms. How many
// connections does it need? Now recompute for a 200 ms hold.
//
// connections = requestsPerSecond * (holdMillis / 1000)
// TODO 2: build the pool. A Semaphore(size, true) plus a method that
// tryAcquire(timeout), sleeps for holdMillis, and releases in a
// finally. Count served, timed out, and peak in use.
// TODO 3: fire 50 requests at a pool of 10, each holding for 100 ms,
// with a 250 ms acquisition timeout.
//
// Predict served and refused BEFORE running it. The arithmetic is
// exact: how many 100 ms rounds fit inside 250 ms?
// TODO 4: check peak in use. It cannot exceed the pool size — confirm
// that, because it is what makes the pool a concurrency limit rather
// than a capacity.
// TODO 5: the failures in TODO 3 never reached a database. Say what
// exception a real HikariCP throws here, and why that is a different
// problem from a slow query even though the latency graph looks the
// same.
// TODO 6: now the important one. Keep everything identical but hold
// the connection for 300 ms instead of 100 — as if a 200 ms remote
// call happened inside it. Compare served counts.
//
// Then say which endpoints in a real service are affected. The answer
// is not "the one that calls the remote service".
// TODO 7: raise the pool to 50 and re-run TODO 6. Everything is served.
// Say why that is not the fix, and what it costs the database.
// TODO 8: instead, raise the acquisition timeout to 60 seconds and
// re-run TODO 3. Nothing is refused. Say what has actually changed
// from a user's point of view, and which of the two you would rather
// deploy.
}
}Run it locally:
cd exercises/java/db-performance/connection-pool-sizing/01-warmup
javac Starter.java -d /tmp/out && java -cp /tmp/out StarterDone when
- You computed pool size from a rate and a hold time and checked it against a run
- You exhausted a real pool and counted the acquisition failures
- You predicted the served/refused split from the timeout and hold time before running it
- You showed the same pool serving far fewer requests when a remote call is held inside it