Fix “Pool Empty. Unable to Fetch a Connection” Errors in Java Applications

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:

  1. Connection leaks — some database connections are not being closed after use.
  2. Inefficient SQL queries — long-running queries keep connections busy.
  3. Improper pool sizing — the configured pool is too small for the workload.
  4. High concurrency — a sudden spike in parallel requests.
  5. 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-size if your database supports more concurrent connections.
  • Set connection-timeout to a reasonable value (e.g., 20 seconds).
  • Monitor max-lifetime to 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

ProblemAction
Connections not releasedUse try-with-resources or close manually
Too small pool sizeIncrease maximum-pool-size
Slow queriesOptimize SQL and indexes
Connection leaksEnable leak detection
Unbalanced loadScale 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.
This article is inspired by real-world challenges we tackle in our projects. If you're looking for expert solutions or need a team to bring your idea to life,

Let's talk!

    Please fill your details, and we will contact you back

      Please fill your details, and we will contact you back