The full picture
spring.datasource.hikari:
maximum-pool-size: 15 # from math, not vibes: see below
minimum-idle: 15 # fixed pool: no resize thrash under burst
connection-timeout: 2000 # fail fast; don't queue callers into timeout stacking
max-lifetime: 1800000 # < DB/LB idle kill (30m) — avoids mid-query drops
keepalive-time: 300000
leak-detection-threshold: 10000 # logs stacks holding >10s — finds the OSIV/remote-call bugs
# The math: target 500 req/s hitting DB, avg TX hold 20ms
# connections needed ≈ 500 * 0.02 = 10 (+ headroom) => 15
# Fleet: 20 pods * 15 = 300 ≤ DB max_connections budget (with reserve) ✅- Exhaustion triage order: leak detection stacks → who holds long? (remote call in TX? OSIV? missing close on manual usage?) → fix holds → only then revisit size. Doubling the pool with 5s holds just moves the queue into the DB.
- Timeout chain coherence: client timeout > server request timeout > (connectionTimeout + query timeouts). Inverted chains create work that outlives its requester — the retry-storm amplifier.
- Virtual threads sharpen this: VTs remove the thread ceiling, so 10k concurrent requests all reach for the pool — the pool becomes the explicit concurrency limiter; consider a semaphore in front of scarce resources to keep queues visible and bounded.
- Observe:
hikaricp.connections.pending(callers waiting) is the leading indicator; alert on sustained pending, not on usage %.