← Benchmarks

Connection Pooling Benchmarks

The cost of arriving without a reservation. What happens when 200 clients show up at a database that seats 100, and how pooling changes the arithmetic.

The Waiter of Gold Lapel · Updated Mar 20, 2026 Published Mar 5, 2026 · 5 min read
A TCP handshake, a TLS negotiation, and a PostgreSQL authentication walk into a latency measurement.

Methodology

These benchmarks isolate connection overhead from query execution time. The test query is SELECT 1 — the simplest possible query, ensuring that all measured latency is connection and protocol overhead, not database work.

The "direct" mode opens a new PostgreSQL connection for every query (TCP + TLS + authentication), executes the query, and closes the connection. The "pooled" mode routes through Gold Lapel's built-in connection pool, which maintains persistent backend connections to PostgreSQL.

ParameterValue
PostgreSQL16.2 (max_connections = 100)
Gold Lapel0.1.0 (built-in connection pooling)
CPU4-core AMD EPYC (c5.xlarge)
RAM8 GB
Storagegp3 SSD, 3000 IOPS
NetworkSame VPC, <1ms RTT
Test querySELECT 1 (measures connection overhead only)
Duration60 seconds per test

Where the time goes

A direct PostgreSQL connection involves a TCP handshake, TLS negotiation, and the PostgreSQL authentication protocol — before the first byte of SQL is parsed. Gold Lapel's pool eliminates all three for every query after the first.

# Direct connection: each request opens a new TCP + SSL + PG handshake
# Time breakdown per connection:
#   TCP handshake:     0.3ms
#   TLS negotiation:   1.2ms
#   PG authentication: 0.8ms
#   Query execution:   0.1ms
#   Connection close:  0.1ms
#   Total:             2.5ms (connection overhead dominates)

# Pooled connection: reuses existing authenticated connection
# Time breakdown per query:
#   Query dispatch:    0.1ms  (connection already open)
#   Query execution:   0.1ms
#   Result return:     0.1ms
#   Total:             0.3ms

Results

Concurrent clientsDirect TPSPooled TPSDirect latency (P50)Pooled latency (P50)Throughput gain
102,8409,4203.5ms1.1ms3.3x
504,12034,80012.1ms1.4ms8.5x
1003,89038,20025.7ms2.6ms9.8x
200Connection refused36,400N/A5.5ms-
500Connection refused33,100N/A15.1ms-

At 200+ concurrent clients, direct connections are refused because PostgreSQL's max_connections is set to 100. The pool multiplexes 500 application clients onto 20 backend connections.

Analysis

The throughput improvement scales with concurrency because the bottleneck shifts from connection creation to connection contention.

  • At 10 clients: Direct connections are viable. Each client gets a dedicated backend connection, and the overhead per connection (2.5ms) is tolerable. Pooling still provides a 3.3x improvement by eliminating per-query connection setup.
  • At 50-100 clients: The improvement curve steepens (8.5x–9.8x). Direct connections now compete for PostgreSQL's connection slots and memory. Each backend connection consumes approximately 10 MB of RAM, so 100 connections consume 1 GB — a significant fraction of the 8 GB available.
  • At 200+ clients: Direct connections fail entirely. The pool continues serving requests by queuing application clients and dispatching them to available backend connections. Latency increases (5.5ms at 200 clients, 15.1ms at 500) but remains far below direct connection overhead, and no requests are refused.

Pool sizing

Gold Lapel's default pool size is min(4 * CPU_cores, max_connections / 2). For this benchmark (4 cores, max_connections=100), the pool maintains 16 backend connections. This is sufficient for 500 concurrent clients because the test query executes in under 1ms — each backend connection can serve hundreds of queries per second.

For real workloads with longer query execution times, the pool size should be tuned based on the expected concurrent query volume and average query duration. The pool automatically adjusts between a minimum of 4 and the configured maximum.

How to reproduce

# Clone the benchmark suite
git clone https://github.com/goldlapel/benchmarks
cd benchmarks/connection-overhead

# Start test environment (PG with max_connections=100)
docker compose up -d

# Run direct connection benchmark
./run.sh --mode direct --clients 10,50,100,200,500 --duration 60

# Run pooled connection benchmark (through Gold Lapel)
./run.sh --mode pooled --clients 10,50,100,200,500 --duration 60

# Compare results
./compare.sh results/

The benchmark uses pgbench in direct mode and a custom client for pooled mode. The comparison script generates a CSV report with latency percentiles.

Terms referenced in this article

The pool sizing question these numbers raise has a practical treatment for every major framework. If you are running Spring Boot, the open-in-view pool exhaustion guide shows how the default configuration defeats even a well-sized pool. If Rails, the connection pool sizing guide reveals the arithmetic behind Puma and Solid Queue.