Recommended Free Tools
A database connection pool is exhausted when callers cannot obtain a connection within the configured wait period. That error identifies a bottleneck, not its cause: slow queries, long-lived transactions, leaked connections, database limits, an undersized pool, or even a connection setup failure can all produce similar symptoms. Find which layer is waiting before changing pool size.
What “pool exhausted” means
An application pool keeps a bounded set of database connections available for reuse. When every connection is in use, new callers wait; if they cannot get a connection before the acquisition timeout, the request fails. A full pool may be too small for useful concurrency, but it may also be full because connections are held too long or the database cannot process work fast enough.
First identify where the wait occurs: in the application pool, a pooler’s client queue, its server-connection pool, or at the database’s connection limit. These are different constraints and require different fixes.
Diagnose the constrained layer
1. Capture the incident and pool state
Record the exact error and timestamp, application pool and library version, database version, and whether the failure happens during startup or only under load. During the incident, inspect connection-acquisition wait time, active and idle connections, pending callers, and the configured pool maximum.
#1 Best Overall
2. Correlate pool metrics with database work
Compare pool activity with query latency, transaction duration, lock waits, database CPU and I/O, and the database’s total connection count. Pool metrics alone can be misleading: a saturated pool does not show whether connections are doing useful work, waiting on locks, or sitting in open transactions.
3. Check each connection limit in the path
If the application connects through PgBouncer, inspect both its client-side limits and its server-side pool limits, as well as queueing and timeouts. PgBouncer documents max_db_connections for server connections and max_db_client_connections for clients; client connections may queue while waiting for active server connections. Check the documentation for the deployed PgBouncer release because settings and defaults can vary: PgBouncer configuration.
PostgreSQL’s max_connections is a server limit set at startup. PostgreSQL 18 documentation says the typical default is 100, but it can be lower depending on kernel support. Raising it is not free: a higher limit increases resource allocation, including shared memory, and reserved slots may also affect which connections can enter. Check the server’s actual configuration and available headroom rather than assuming the typical default applies: PostgreSQL 18: Connections and Authentication.
Rank #2
- Upgraded Two Zipper Pockets: Forvencer server books feature two secure zipper pockets for better organization of coins, cash, and receipts, ensuring that everything you collect has a safe and secure place
- Smart Storage & Quick Access: Designed with 8 multi-functional compartments, the right side includes a guest receipt pad, while the left has a money pocket, ticket pocket, and credit card slot. Two small clear pockets store bills, receipts, and other visible items. A stitched pen loop ensures you always have your favorite pen ready
- High-quality & Easy to Clean: Crafted from high-quality PU leather with heavy-duty stitching, this server book is built to last. It resists tears, scratches, and its waterproof surface makes cleaning easy with just a damp cloth or a non-chlorine sanitizer
- Perfect Fit for Your Apron: Measuring 5” x 8”, this compact organizer is slightly smaller than other models, making it ideal for bending or sitting while carrying in your server apron. It holds everything a waitress needs—a place for everything
- What's Included: This server organizer comes with multiple open and zippered pockets to store money, receipts, tips, etc. Clear sleeves are perfect for keeping menus or special lists while serving. Available in a variety of colors, allowing you to express yourself even when in uniform
4. Investigate connection hold time and workload
- Look for slow queries, lock waits, and transactions that run longer than expected.
- Check for idle-in-transaction sessions. An open transaction can hold locks and prevent PostgreSQL vacuum from removing row versions that remain visible to that transaction.
- Review whether every code path returns or closes its connection, including error and cancellation paths.
- Check for connection storms after scaling events and calculate the total pool capacity across all application processes or nodes, not just one instance.
5. Separate runtime saturation from connection setup failure
If connections cannot be created, verify the host, port, credentials, TLS settings, driver, and connection URL. Try a minimal direct connectivity check and inspect the first underlying exception before changing the pool maximum. A setup or initialization failure can look like a pool problem even when the database is not saturated.
HikariCP’s guide discusses measurement and common connection errors, but those pages are third-party guidance rather than verified official HikariCP project documentation: HikariCP performance tuning and HikariCP FAQ.
Common causes and the fixes that match them
Connections are leaked or held too long
Return connections reliably on success, error, and cancellation paths. Keep transactions focused on database work; do not keep one open while waiting on a user, a remote API, or other unrelated activity. For PostgreSQL, idle_in_transaction_session_timeout can terminate sessions idle inside a transaction. Apply it with a session- or role-appropriate policy and account for how the application and middleware handle a terminated session.
Queries or lock waits occupy connections
Inspect slow SQL, blocking sessions, and transaction patterns before increasing concurrency. Optimize the query or address the blocking work. PostgreSQL provides distinct controls: statement_timeout limits statement execution, lock_timeout limits time waiting for a lock, and transaction_timeout limits the time a session spans within a transaction. These controls bound different conditions; they do not make slow work efficient. Global settings can affect every session, so choose values that fit the application’s latency budget and scope them carefully. See PostgreSQL 18: Client Connection Defaults.
The application pool is too small for useful concurrency
Increase the pool cautiously only when measurements show callers are queueing at the application pool and the database has capacity for additional productive work. Test through the same application-to-database network path used in production. Stop increasing concurrency when throughput stops improving or latency worsens.
The fleet exceeds the connection budget
Each process or node may have its own pool, and other services, administrative sessions, and intermediary poolers consume connections too. Estimate the combined maximum across the fleet before changing a per-instance setting. Posit Connect’s PostgreSQL administration documentation illustrates how per-node pools multiply connections to the same database; its pool defaults are specific to Posit Connect, not general recommendations: Posit Connect: PostgreSQL administration.
Rank #4
If PostgreSQL connection slots are exhausted, account for all clients and maintain operational headroom before considering a change to max_connections. A higher server limit uses more resources and may simply let more work compete for the same database capacity.
Many clients compete for a smaller useful server pool
A pooler such as PgBouncer can accept many client connections while limiting the number of server connections used for database work. This can help when client demand exceeds useful database concurrency, but it moves queueing into the pooler. Monitor its client and server limits, queues, and timeout behavior, and verify that the chosen pool mode is compatible with the application’s transaction and session features.
Timeouts are mismatched across layers
Application request deadlines, pool acquisition waits, pooler timeouts, and database-side limits should work together. A short wait can fail requests quickly without resolving the underlying queue; a long wait can tie up application resources. Test timeout behavior with the application and middleware, especially where a database timeout terminates a session or transaction. Use timeouts to bound waiting or resource holding, not as substitutes for fixing leaks, slow queries, or overload.
Best Value
Choosing between a larger pool, a pooler, and a database limit change
Compare the options using evidence from the same workload rather than relying on a universal pool-size rule.
| Option | Where work may queue | What to verify |
|---|---|---|
| Increase the application pool | At the application pool if it is currently the bottleneck; additional active work reaches the database. | Productive throughput, query latency, database CPU and I/O headroom, and the combined pool maximum across all instances. |
| Add or tune a pooler such as PgBouncer | At the pooler’s client queue when client demand exceeds its server-connection budget. | Client and server limits, queue and timeout behavior, workload compatibility with pool mode, and PostgreSQL capacity. |
Raise PostgreSQL max_connections |
At the database connection limit, if slots are genuinely the constraint; more sessions may also compete for database resources. | All clients and reserved operational headroom, resource implications, and whether the database can handle the additional concurrency. |
No single pool size fits every workload. Size application pools and any pooler’s server budget around measured productive concurrency, then include every application instance and other database client in the total connection budget.
Quick Recap
Apply changes safely
- Establish a baseline: capture pool waits, active and idle counts, pending callers, query and transaction latency, database CPU and I/O, and total connections during the failure.
- Confirm the bottleneck: determine whether the application pool, pooler client queue, pooler server pool, database slots, or connection setup is responsible.
- Make one targeted change: fix the observed cause, such as returning leaked connections, reducing transaction duration, optimizing blocked work, or cautiously adjusting a limit.
- Load-test the production path: evaluate throughput and latency with the same application-to-database route and observe database and pooler headroom.
- Keep or revert based on results: retain a change only if it improves the failure mode without degrading latency or exhausting another layer.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




