HikariCP’s default settings are so aggressive that they can actually hurt performance under load by causing unnecessary connection churn.
Let’s watch a typical HikariCP pool in action. Imagine we have a web application that gets hit with a sudden surge of requests. Each request needs a database connection.
// In your application's configuration (e.g., application.properties/yml)
spring.datasource.url=jdbc:postgresql://localhost:5432/mydatabase
spring.datasource.username=myuser
spring.datasource.password=mypassword
spring.datasource.hikari.poolName=MyWebAppPool
spring.datasource.hikari.maximumPoolSize=10
spring.datasource.hikari.minimumIdle=5
spring.datasource.hikari.connectionTimeout=30000 // 30 seconds
spring.datasource.hikari.idleTimeout=600000 // 10 minutes
spring.datasource.hikari.maxLifetime=1800000 // 30 minutes
When a request comes in, it asks the MyWebAppPool for a connection. If there’s an idle connection available, HikariCP hands it over instantly. If not, and MyWebAppPool hasn’t reached maximumPoolSize (10 in this case), it’ll try to create a new one. If it’s already at 10, the request waits up to connectionTimeout (30 seconds) for a connection to become free.
The real magic, and the source of much confusion, is how HikariCP manages idle connections. minimumIdle (5) tells HikariCP to always try and keep at least 5 connections ready to go, even if no requests are active. idleTimeout (10 minutes) is the maximum time a connection can be idle before HikariCP considers closing it, but it will only do so if closing it doesn’t drop the pool below minimumIdle. maxLifetime (30 minutes) is the absolute maximum age a connection can reach before it must be retired, regardless of its idle status, to prevent resource leaks or stale connections.
The Problem It Solves: Database Connection Bottlenecks
Applications frequently need to talk to a database. Establishing a new database connection is an expensive operation. It involves network round trips, authentication, and resource allocation on both the application and database servers. Doing this for every single request is a recipe for disaster. A connection pool, like HikariCP, acts as a cache for these expensive connections, making them available on demand and reducing latency.
Internal Mechanics: Fast and Smart
HikariCP is built for speed. It uses a highly optimized ConcurrentBag for managing connections, minimizing contention. Its "just-in-time" connection creation means it only spins up new connections when absolutely necessary, up to maximumPoolSize. The idle connection management is designed to balance resource usage with readiness. It’s not just about keeping connections open; it’s about intelligently deciding which ones to keep, which to close, and when to create new ones based on demand and configuration.
Tuning for Production
The defaults are a starting point, but production environments have specific needs.
-
maximumPoolSize: This is the hard cap. A common mistake is setting this too high, thinking "more is better." Too many connections can overwhelm your database. Start with a reasonable number based on your expected peak load and database capacity. A good starting point is often(core_count * 2) + 1for CPU-bound workloads, but for I/O-bound (like database access), it might be higher. Monitor your database’s connection count and CPU usage. IfmaximumPoolSizeis 50, and your database is struggling with 50 connections, you’ve found your problem.- Diagnosis:
SHOW max_connections;on your PostgreSQL/MySQL server. Monitorpg_stat_activityorSHOW PROCESSLIST;. - Fix:
spring.datasource.hikari.maximumPoolSize=20(Example value). - Why it works: Prevents overwhelming the database server with too many concurrent connections, which can lead to performance degradation or outright connection failures.
- Diagnosis:
-
minimumIdle: This is crucial for handling sudden traffic spikes. IfminimumIdleis too low (e.g., 0 or 1) and your pool is empty, the first few requests will have to wait for new connections to be established, causing latency. Set it to a level that can absorb your typical burst of traffic. Often, a value between 2 and 10 is sufficient, depending on your application’s nature.- Diagnosis: Observe request latency during initial traffic bursts. If the first few requests are significantly slower,
minimumIdlemight be too low. - Fix:
spring.datasource.hikari.minimumIdle=5(Example value). - Why it works: Ensures a baseline number of ready-to-use connections are always available, smoothing out initial latency during traffic surges.
- Diagnosis: Observe request latency during initial traffic bursts. If the first few requests are significantly slower,
-
connectionTimeout: This is how long a thread will wait for a connection before throwing an exception. The default of 30 seconds is often too long for user-facing applications. Users don’t want to wait half a minute for a page to load. A value between 5 and 10 seconds is more appropriate for interactive applications.- Diagnosis: Look for
HikariPool.getConnection() timed outerrors in your logs. - Fix:
spring.datasource.hikari.connectionTimeout=5000(5 seconds). - Why it works: Fail fast. If a connection isn’t available within a reasonable time, it’s better to inform the user quickly than to tie up threads and resources for an extended period.
- Diagnosis: Look for
-
idleTimeout: This setting controls how long an idle connection can stay in the pool before HikariCP tries to close it. The default of 10 minutes is often fine, but if you have very spiky traffic and want to reclaim resources more aggressively, you might lower it. However, be careful not to set it too low, as it can lead to frequent connection cycling, which is also inefficient. A common mistake is setting this too low andminimumIdletoo high, causing HikariCP to constantly close and reopen connections.- Diagnosis: Monitor connection creation/destruction events in HikariCP metrics (if enabled) and observe database connection churn.
- Fix:
spring.datasource.hikari.idleTimeout=60000(1 minute). - Why it works: Allows the pool to shrink more quickly during periods of low activity, freeing up database resources, but only if the pool size remains above
minimumIdle.
-
maxLifetime: This is a critical setting for preventing stale connections and ensuring that connections are periodically re-established. Database servers or network intermediaries (like load balancers) might have their own timeouts for idle connections. IfmaxLifetimeis longer than these underlying timeouts, your application might try to use a connection that the database has already closed, leading toconnection resetorbroken pipeerrors. SettingmaxLifetimeslightly less than the shortest expected intermediary timeout is a good practice. For example, if you know your load balancer drops idle connections after 30 minutes, setmaxLifetimeto 29 minutes.- Diagnosis: Look for
Connection resetorBroken pipeerrors in your application logs, especially after periods of inactivity. - Fix:
spring.datasource.hikari.maxLifetime=1740000(29 minutes). - Why it works: Proactively retires connections before they can become stale due to external network or database timeouts, preventing unexpected connection errors.
- Diagnosis: Look for
One of the most counterintuitive aspects of idleTimeout is that HikariCP will not close an idle connection if doing so would cause the number of active and idle connections to drop below minimumIdle. This is a safety net to ensure you always have your desired minimum available, but it means that if your idleTimeout is shorter than your maxLifetime, and your pool is constantly hovering just above minimumIdle, connections might never actually be closed by idleTimeout, only by maxLifetime.
The next common issue you’ll face after tuning connection pools is understanding how your ORM (like Hibernate or JPA) interacts with the pool, specifically its own caching and transaction management policies.