The three numbers that bound a pool
- Load.By Little's law, the connections in use at any moment equal the arrival rate times the time each one is held. 200 requests per second that each hold a connection for 25 ms keep about 5 connections busy.
- Deadlock safety. If one thread can hold two connections at once — typically a
REQUIRES_NEWcall inside an open transaction — a pool smaller thanTn × (Cm − 1) + 1can lock up with every thread holding one connection and waiting for a second. With Cm = 1 the minimum is 1. - The database cap. All instances share one
max_connections. Leave room for admin sessions, replication and batch jobs, and count the extra pods that run during a rolling update.
Worked example
With the calculator's default inputs — 200 requests per second per instance, 25 ms hold time, 30% headroom, 3 instances plus 1 during deploys, and a database with 151 max connections of which 10 are reserved:
| Check | Calculation | Result |
|---|---|---|
| Load-based | 200 × 0.025 s × 1.3 | 7 |
| Deadlock-safe minimum | 1 connection per thread | 1 |
| DB cap per instance | (151 − 10) ÷ (3 + 1) | 35 |
| DB guideline (total) | 8 cores × 2 + 1 | 17 |
| Recommended | max(load, deadlock), capped by the DB | 7 |
7 connections per instance looks small next to the default of 10, and that is the point: the service is fast because each request releases its connection quickly. If you need 50, the fix is usually in the transaction, not the pool.
Measuring connection hold time
Don't guess the hold time. With Spring Boot Actuator and Micrometer, HikariCP publishes these metrics for each pool:
| Metric | What it tells you |
|---|---|
hikaricp.connections.usage | How long connections are held — use the mean or p99 as the hold time |
hikaricp.connections.acquire | How long callers wait to get a connection |
hikaricp.connections.active | Connections in use right now |
hikaricp.connections.pending | Threads waiting for a connection — should stay near zero |
hikaricp.connections.timeout | Requests that gave up after connection-timeout |
HikariCP defaults worth knowing
| Property (spring.datasource.hikari.*) | Default | Note |
|---|---|---|
maximum-pool-size | 10 | Per instance |
minimum-idle | same as maximum | Leave unset for a fixed pool |
connection-timeout | 30000 ms | Many teams lower it to a few seconds to fail fast |
idle-timeout | 600000 ms | Only applies when minimum-idle is below maximum |
max-lifetime | 1800000 ms | Keep it shorter than any DB or proxy idle timeout |
leak-detection-threshold | 0 (off) | Set e.g. 60000 in staging to log leaked connections |
When a bigger pool is the wrong fix
- Remote calls inside a transaction. An HTTP call inside
@Transactionalholds the connection for the whole round trip. Move it outside the transaction and the required pool size drops with it. - Open Session in View.
spring.jpa.open-in-viewis on by default and keeps a connection for the whole web request, including view rendering. Turning it off often cuts hold time sharply. - Slow queries. A query that takes 400 ms instead of 4 ms needs a hundred times the connections at the same traffic. An index is cheaper than a larger database.