Connection Pool Exhausted

Connection pool sizing, max_connections, PgBouncer, RDS Proxy, idle connections, and leak detection.

Overview

Overview

Database connection pools are finite. When the pool is exhausted, new requests queue for a connection, and past the timeout they fail with "connection pool timeout" or "too many connections". This is one of the most common causes of database outages during traffic spikes.

The math

max_connections defaults to 100 in stock PostgreSQL; RDS derives its default from instance memory. Each connection uses approximately 5-10 MB of RAM on the database server, and the total across all application instances must stay below max_connections.

  • 50 app instances × pool of 10 — 500, five times a limit of 100
  • Same, behind a connection pooler — Many app connections multiplexed onto far fewer database connections

PgBouncer pooling modes

  • Session — One database connection per client session, same as no pooling — Everything works
  • Transaction — The connection returns to the pool after each transaction; N app connections share far fewer database connections — Prepared statements and advisory locks need session mode
  • Statement — Most aggressive — Limited

Failure mechanism: a connection leak

> [!TIP] Monitor idle connections in pg_stat_activity; if idle connections match the pool maximum, there is likely a leak.

Key concepts

max_connections
PostgreSQL hard limit on simultaneous database connections. Exceeding it causes "FATAL: sorry, too many clients already". Each connection uses ~5-10 MB. Increase with caution.
PgBouncer transaction mode
Assigns a database connection only during a transaction. Allows 100 app connections to share 10 database connections if queries are short. Incompatible with prepared statements and session-level settings.
Connection leak
Application code that opens a connection but fails to close it on error paths. Connections fill the pool until it is exhausted. Detect with pg_stat_activity: SELECT count(*), state FROM pg_stat_activity GROUP BY state.
RDS Proxy
AWS-managed connection pooler for RDS/Aurora. Integrates with IAM authentication, Secrets Manager rotation. Particularly valuable for Lambda functions that cannot maintain connection pools between invocations.
Pool sizing formula
Empirical rule: pool size = ((core_count * 2) + effective_spindle_count). For a 4-core database server with SSDs: pool per app instance = 9. Total connections = 9 * app_instances.

When to use it

  • Diagnosing "too many connections" errors during a traffic spike
  • Sizing a connection pool for a microservice at scale
  • Implementing PgBouncer or RDS Proxy to support Lambda or serverless workloads
  • Finding a connection leak in an application that slowly accumulates idle DB connections

Interview questions

  • Your application is getting "too many connections" errors during a traffic spike. How do you resolve it immediately and prevent it long-term?

Sign up free to read the full topic

More Troubleshooting topics

Start free on Cariara · Coding problems · System design studio