Understanding the Error
When working with database connection pools in Java (for example, HikariCP, Apache DBCP, or C3P0), you might encounter an error like:
Pool empty. Unable to fetch a connection in 20 seconds, none available [size:50; busy:50; idle:0; lastwait:20000]
This message indicates that:
- The connection pool size is 50.
- All 50 connections are busy (actively being used).
- There are no idle connections available.
- The application waited 20 seconds for a connection to be returned but timed out.
In short, your application ran out of database connections.
🧠 Root Causes
This error generally occurs due to one or more of the following issues:
- Connection leaks — some database connections are not being closed after use.
- Inefficient SQL queries — long-running queries keep connections busy.
- Improper pool sizing — the configured pool is too small for the workload.
- High concurrency — a sudden spike in parallel requests.
- Transaction management issues — transactions not being committed or rolled back quickly.
✅ Best Practices to Prevent Connection Pool Exhaustion
1. Always Close Connections Properly
Every database connection must be returned to the pool after use.
Use try-with-resources in Java to ensure automatic closure:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
public class UserRepository {
private final DataSource dataSource;
public UserRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
public User findById(int id) {
String query = "SELECT * FROM users WHERE id = ?";
try (Connection connection = dataSource.getConnection();
PreparedStatement stmt = connection.prepareStatement(query)) {
stmt.setInt(1, id);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return new User(rs.getInt("id"), rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
This ensures that connections are automatically closed and returned to the pool.
2. Configure the Connection Pool Properly
Here’s an example using HikariCP in a Spring Boot application (application.properties):
spring.datasource.hikari.maximum-pool-size=50
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.connection-timeout=20000
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.max-lifetime=1800000
👉 Tips:
- Increase
maximum-pool-sizeif your database supports more concurrent connections. - Set
connection-timeoutto a reasonable value (e.g., 20 seconds). - Monitor
max-lifetimeto prevent stale connections.
3. Detect and Debug Connection Leaks
HikariCP can automatically log warnings when connections are held for too long:
spring.datasource.hikari.leak-detection-threshold=20000
This will log a warning if any connection is used for more than 20 seconds without being returned.
Example log:
WARN com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for connection...
4. Use Connection Pool Metrics
Most pools expose metrics via JMX or Micrometer.
In Spring Boot, you can monitor active and idle connections with Actuator:
management:
endpoints:
web:
exposure:
include: health, metrics
Then check:
/actuator/metrics/hikaricp.connections.active
/actuator/metrics/hikaricp.connections.idle
This helps you understand whether the pool size is sufficient.
5. Optimize Queries and Transactions
- Ensure queries use proper indexes.
- Keep transactions short and efficient.
- Avoid holding connections during time-consuming operations (e.g., file I/O or long calculations).
⚙️ Example with Apache DBCP
If you’re using Apache DBCP, you can configure it like this:
import org.apache.commons.dbcp2.BasicDataSource;
public class DataSourceConfig {
public static BasicDataSource getDataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/mydb");
ds.setUsername("user");
ds.setPassword("password");
ds.setMaxTotal(50);
ds.setMaxIdle(10);
ds.setMaxWaitMillis(20000);
return ds;
}
}
DBCP throws a similar error if the pool is exhausted:
Cannot get a connection, pool error Timeout waiting for idle object
🔍 Quick Checklist
| Problem | Action |
|---|---|
| Connections not released | Use try-with-resources or close manually |
| Too small pool size | Increase maximum-pool-size |
| Slow queries | Optimize SQL and indexes |
| Connection leaks | Enable leak detection |
| Unbalanced load | Scale horizontally or use a read-replica DB |
🧩 Conclusion
The error “Pool empty. Unable to fetch a connection” is a clear sign of connection pool exhaustion — a common but solvable performance issue in Java applications.
By closing connections properly, tuning pool size, and monitoring metrics, you can ensure that your system remains stable under load and performs reliably even in peak conditions.
📈 Summary
- Understand what the error means: all connections are busy.
- Detect leaks using HikariCP’s
leak-detection-threshold. - Tune your pool size based on load testing.
- Monitor usage to predict scaling needs early.


