Connection Pooling (HikariCP)
Opening a database connection is expensive, so applications keep a pool of reusable connections instead of creating one per request. Spring Boot ships HikariCP as the default and only-configured pool, valued for its speed and small footprint. You rarely need to add anything: HikariCP is pulled in transitively by spring-boot-starter-data-jpa and spring-boot-starter-jdbc.
How pooling works
When a request needs the database, it borrows a connection from the pool, runs its query, and returns it. If every connection is in use, the caller waits up to connection-timeout for one to free up. Idle connections beyond the minimum are eventually retired. This recycling avoids the TCP and authentication overhead of opening fresh connections on every query.
Configuration
All HikariCP settings live under spring.datasource.hikari.*. Here is a typical production block.
spring:
datasource:
url: jdbc:postgresql://localhost:5432/shopdb
username: shop
password: secret
hikari:
pool-name: shop-pool
maximum-pool-size: 10
minimum-idle: 10
connection-timeout: 30000 # 30s
idle-timeout: 600000 # 10m
max-lifetime: 1800000 # 30m
keepalive-time: 120000 # 2m
Note: Values are in milliseconds. The defaults are sensible for many apps; the most important value to set deliberately is
maximum-pool-size.
Key properties
| Property | Default | Meaning |
|---|---|---|
maximum-pool-size | 10 | Hard cap on total connections (active + idle) |
minimum-idle | = max-pool-size | Minimum idle connections kept ready |
connection-timeout | 30000 | Max ms to wait for a connection before throwing |
idle-timeout | 600000 | Ms an idle connection may sit before retirement |
max-lifetime | 1800000 | Max age of a connection before it is replaced |
keepalive-time | 0 (off) | Interval to ping idle connections to keep them alive |
auto-commit | true | Whether borrowed connections start in auto-commit |
Tip: HikariCP’s author recommends a fixed-size pool: set
minimum-idleequal tomaximum-pool-size. A fixed pool has more predictable latency than one that grows and shrinks under load.
Sizing the pool
Bigger is not better. Each connection consumes resources on the database server, and too many connections cause contention. A widely used starting formula is:
pool size = (core_count * 2) + effective_spindle_count
For a modern app on SSD-backed cloud databases, that often lands around 10. Key guidance:
- Keep
maximum-pool-sizewell under the database’smax_connectionslimit, especially when multiple instances connect. - If you run N application instances, total connections = N ×
maximum-pool-size. Budget accordingly. - Set
max-lifetimea few seconds shorter than any database or load-balancer connection timeout so Hikari retires connections before the server kills them.
Warning: A pool larger than the database can handle leads to slower throughput, not faster. When in doubt, start small (10) and increase only if monitoring shows sustained pool exhaustion.
Diagnosing pool exhaustion
When all connections are checked out and a borrower waits past connection-timeout, Hikari throws:
java.sql.SQLTransientConnectionException: shop-pool - Connection is not available,
request timed out after 30000ms.
This almost always means connections are being held too long (long transactions, missing @Transactional boundaries, or leaks), not that the pool is too small. Investigate query duration before bumping maximum-pool-size.
Monitoring
HikariCP exposes pool metrics through Micrometer when Spring Boot Actuator is on the classpath. Enable metrics and they appear automatically.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Useful gauges include:
| Metric | Meaning |
|---|---|
hikaricp.connections.active | Connections currently in use |
hikaricp.connections.idle | Connections available |
hikaricp.connections.pending | Threads waiting for a connection |
hikaricp.connections.timeout | Count of borrow timeouts |
hikaricp.connections.usage | Time a connection is held before return |
A persistently high pending count or any timeout increments are the clearest signals that the pool is undersized or connections are leaking.
management:
endpoints:
web:
exposure:
include: health, metrics
endpoint:
health:
show-details: when-authorized
Best Practices
- Use a fixed-size pool (
minimum-idle=maximum-pool-size) for predictable latency. - Keep total connections across all instances under the database’s connection limit.
- Set
max-lifetimeshorter than the database’s idle-connection timeout. - Monitor
pendingandtimeoutmetrics via Actuator rather than guessing. - Fix long-held connections (large transactions, N+1 queries) before enlarging the pool.