Performance & Capacity Practical By Samson Tanimawo, PhD Published Oct 17, 2025 4 min read

Connection Bottlenecks

Pool exhaustion.

Identifying connection bottlenecks

Application latency spike with no underlying database or service issue. Connection pool waits show up as application latency, not service latency.

Connection pool wait time metric exceeds zero. The application is queueing for connections.

Connection refused errors at the database or downstream service. Either max connections reached or short-lived connection churn.

Connection pool sizing

Formula: max_connections * worker_processes / total_app_processes. Allocate connections to each worker conservatively.

Default pool sizes are usually wrong. Postgres default 100; pgbouncer can multiplex. Right-size based on real concurrency.

Watch for idle in transaction. Long-running transactions hold connections; pool exhaustion follows.

TCP-level bottlenecks

Operating system TCP connection limits: ulimit -n for file descriptors. Default 1024 is often too low for production.

Time-Wait state accumulation. Many short-lived connections leave sockets in time-wait. Tune tcp_tw_reuse if appropriate.

Ephemeral port range. Default 32768-60999 caps simultaneous connections to a single destination. Tune with care.

Multiplexing patterns

HTTP/2 connection multiplexing reduces connection count. Single connection serves many requests concurrently.

gRPC over HTTP/2 inherits this. Per-server connection count drops from hundreds to tens.

Connection pooling at the framework level. Database connection poolers (pgbouncer, ProxySQL) multiplex client connections to fewer database connections.

Monitoring connection state

Pool utilisation: in-use connections versus pool size. Above 80% sustained means pool is too small.

Connection acquire latency. Time spent waiting for a connection.

Per-destination connection count. Detect when one downstream is hogging connections.