Verified by Garnet Grid

Database Connection Pooling

Optimize database connections. Covers connection pool architecture, PgBouncer, HikariCP, pool sizing formulas, connection leak detection, and serverless connection challenges.

Every database connection costs memory (5-10 MB in PostgreSQL), a process or thread on the server, and network overhead. Without pooling, 100 application instances each opening 10 connections creates 1,000 database connections — far more than most databases can handle efficiently. Connection pooling reuses connections across requests, keeping database load manageable.


Why Connection Pooling

Without PoolingWith Pooling
New connection per request (50-100ms overhead)Reuse existing connection (< 1ms)
1,000 connections for 100 app instances50 pooled connections serve 100 instances
Database memory exhaustedBounded, predictable memory usage
Connection storms on deploymentGraceful ramp-up

Pool Sizing Formula

Optimal pool size = ((core_count * 2) + effective_spindle_count)

For a 4-core server with SSD:
Pool size = (4 * 2) + 1 = 9

For most applications:
Start with: connections = (CPU cores × 2) + 1
Then benchmark and adjust.

Key insight: MORE connections ≠ MORE throughput.
Too many connections cause lock contention and
context switching that REDUCES performance.

PgBouncer Configuration

[databases]
myapp = host=postgres port=5432 dbname=myapp

[pgbouncer]
listen_port = 6432
listen_addr = *

; Pool mode
pool_mode = transaction    ; Best for most apps

; Pool sizes
default_pool_size = 20     ; Connections per database/user pair  
max_client_conn = 1000     ; Max client connections to PgBouncer
min_pool_size = 5          ; Keep minimum connections warm
reserve_pool_size = 5      ; Emergency connections

; Timeouts
server_idle_timeout = 600  ; Close idle server connections after 10 min
client_idle_timeout = 0    ; Don't close idle client connections
query_timeout = 30         ; Kill queries running > 30 seconds

; Connection lifetime
server_lifetime = 3600     ; Recycle connections every hour

Pool Modes

ModeBehaviorBest For
SessionConnection held for entire sessionLong-lived connections, prepared statements
TransactionConnection returned after each transactionMost web applications (recommended)
StatementConnection returned after each statementSimple queries, no multi-statement transactions

Anti-Patterns

Anti-PatternProblemFix
No poolingConnection per request, exhausts DBPgBouncer or application-level pooling
Pool too largeLock contention, context switchingFormula-based sizing, benchmark
Pool too smallRequests queue waiting for connectionsMonitor wait time, increase if needed
Connection leaksPool exhausted, app hangsLeak detection, connection timeout
Session pooling for web appsConnections held too longTransaction pooling mode

Checklist

  • Connection pooler deployed (PgBouncer for PostgreSQL)
  • Pool mode: transaction (for web apps)
  • Pool size: formula-based, benchmarked
  • Connection leak detection enabled
  • Monitoring: active connections, wait time, pool utilization
  • Query timeout configured
  • Connection lifetime recycling (prevent stale connections)
  • Serverless: connection pooler required (Neon, Supabase pooler)

:::note[Source] This guide is derived from operational intelligence at Garnet Grid Consulting. For database consulting, visit garnetgrid.com. :::

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →