Connection pool sizing
How big should the database connection pool be? Little's law and Erlang C from your request rate and connection hold time, the PostgreSQL wiki's cores × 2 + spindles explained, every instance's pool against max_connections (RDS defaults included), a simulation of a pool too small and too large, and HikariCP's settings with maxLifetime and keepalive worked out from the idle limits on your network path.
Added
Works with
- Spring Boot inspector Databases and streamingopens the Hikari settings worked out here in the Spring Boot config checker
How to use it
The pool that is "obviously" too small is often the right size, and the generous one is often what takes the database down in a burst. This page works the size out four ways: from your load, from the database’s cores, from the connection limit, and by simulating it.
How to use it
-
Size it — requests per second for one instance and how long each holds a connection. Little’s law gives the connections busy on average (
L = λW); a pool of exactly that is 100% busy, so the table treats the pool as an M/M/c queue (Erlang C) and shows, for each size, how busy it is, the chance a request waits at all, the mean and p99 wait, and the chance of waiting longer thanconnectionTimeout. The suggestion is the smallest pool with a p99 wait under a tenth of the hold time and no more than 85% busy. The hold time is the whole time a connection is out — usually the whole@Transactionalmethod, and with Spring’s defaultopen-in-viewthe rest of the request — not the query time; Hikari’shikaricp.connections.usagemetric measures it. -
Database limit — the PostgreSQL wiki’s starting point,
connections = (cores × 2), with what it does and doesn’t mean (it is active connections for the whole server, cores are physical, spindles are a guess for SSDs). Then every instance’s pool, the extra instances during a rolling deploy or at the autoscaling maximum, and other clients (migrations, admin tools, jobs) against
effective spindlesmax_connectionsless the reserved slots, drawn as a bar, with the largest pool per instance that fits. Pick an Amazon RDS or Aurora class to fill in its defaultmax_connectionsfrom the parameter-group formula. -
Simulate — a discrete-event simulation: requests arrive at random, round-robin across the instances; each borrows a connection from its instance’s pool (or waits for one, and fails after
connectionTimeout), runs its work on the database and holds the connection a little longer for app code. The database runs every active query at once on its cores; past the core count they share, and a contention factor β makes total throughput fall as more compete. Sweep pool sizes runs the same arrivals for a range of sizes and plots the p99, the median and the timeouts against pool size: too small queues and times out in the application, too large moves the queue into the database in a burst. Run shows one size over time — connections in use, requests waiting, queries active against the cores, and p99 and timeouts per second. Presets set up the cases worth seeing. -
Hikari settings — tick the things on your network path that close connections (an AWS NLB’s 350 s idle timeout, an ALB, NAT gateways, PgBouncer’s
server_idle_timeoutandserver_lifetime, RDS Proxy, MySQL’swait_timeout…) and correct their numbers.maxLifetimeis set under any lifetime limit andkeepaliveTimeunder any idle limit, within Hikari’s 30-second minimums. You get the Spring Bootapplication.ymlor.propertiesto copy, and Check in the Spring Boot inspector opens it in that tool’s checker. Below, every Hikari setting explained with its default.
Defaults and limits are those of HikariCP 5.x (Spring Boot 3.3 ships 5.1.0) and 6.x; I read them from
the jars. keepaliveTime is off by default in 5.x and two minutes from 6.0.
Limits
-
Erlang C assumes random (Poisson) arrivals and exponentially distributed hold times, one queue per pool and requests that wait rather than give up. Real traffic is burstier; use the simulator with a burst, and your own metrics.
-
The database model is deliberately simple: queries share cores fairly, and β stands in for everything that makes a busy database slower than its core count suggests (lock waits, context switches, buffer and cache contention, I/O queues). It shows the shape, not your numbers. Measure throughput and latency on your own system as you change the pool.
-
Hikari’s pool is treated as fixed-size and FIFO; connection creation, validation and the `ConcurrentBag’s thread-local reuse are not modelled.
-
RDS values use the instance’s nominal memory;
DBInstanceClassMemoryis a little lower, so the real default is a little lower too.
Sources
-
HikariCP wiki, About Pool Sizing and the configuration reference.
-
PostgreSQL wiki, Number Of Database Connections.
-
Amazon RDS User Guide, “Maximum number of database connections” (the
max_connectionsparameter-group formulas). -
J. D. C. Little (1961) and A. K. Erlang (1917) via the queueing planner, whose engine this page uses for Erlang C.
Related: capacity and queueing planner (M/M/c in depth: percentiles,
service levels, the utilisation knee), PostgreSQL playground (try
SHOW max_connections, pg_stat_activity and locking for real), Spring
Boot inspector (Hikari keys checked, actuator metrics), latency numbers
and load balancing and resilience.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser; nothing you type leaves this page.