Connection Pooling
Reusing a fixed set of established database connections instead of opening one per request — the difference between a database that scales and one that falls over at 100 connections.
Last reviewed: July 25, 2026
What is connection pooling?
Opening a database connection is expensive: TCP handshake, TLS negotiation, authentication, and server-side session setup — tens of milliseconds, plus real memory on the database. Connection pooling maintains a set of already-established connections that requests borrow and return, amortizing that setup cost across thousands of queries. Instead of open-query-close per request, the app checks a live connection out of the pool, uses it, and hands it back.
Why it’s not optional at scale
Databases cap connections — a default Postgres handles a few hundred, and each idle connection still consumes memory. Without pooling, a modestly busy app opens connections faster than it closes them and exhausts the limit, at which point new connections fail entirely — the classic “too many connections” outage that looks like a database crash but is a client-side discipline failure. Pooling bounds concurrency: 20 pooled connections can serve thousands of requests/second because each query holds a connection for milliseconds. The pool size, not the request rate, is what the database sees.
The serverless amplification problem
Serverless broke the classic model. Each Lambda instance runs its own pool, and hundreds of concurrent instances mean hundreds of pools mean connection exhaustion — the exact failure pooling was meant to prevent, recreated at the platform layer. This is why RDS Proxy, PgBouncer, and Supabase’s pooler exist: a shared external pooler sits between the fleet and the database, multiplexing many client connections onto few database ones. PgBouncer’s transaction-mode pooling (a connection is borrowed only for the duration of a transaction, not a whole session) is the standard high-density answer — with the catch that session-level features (prepared statements, SET state) need care.
What people get wrong
- No pooler in serverless — the “too many connections” outage is nearly guaranteed at scale.
- Oversized pools: 500 connections “to be safe” can overwhelm the database’s memory; size to the DB’s real capacity.
- Transaction-mode surprises — session state doesn’t survive; prepared statements and temp tables break silently.
Primary source: PgBouncer documentation
Why Databases Need Connection Pooling Specifically
Establishing a new database connection is relatively expensive — it involves a TCP handshake, often a TLS handshake, and authentication, all of which add latency that becomes especially costly if repeated for every single query rather than once per reused connection. Connection pooling maintains a set of already-established, ready-to-use connections that application code borrows from and returns to as needed, avoiding this per-query connection overhead. It’s particularly critical for databases with a hard limit on total concurrent connections (as most relational databases have), since without pooling, a burst of traffic or a serverless application scaling to many concurrent instances can easily exhaust the database’s connection limit — a common failure mode that dedicated connection pooling layers like PgBouncer or AWS RDS Proxy exist specifically to prevent.
Historical figures and technical concepts for informational purposes only. Not technical, professional, legal, or financial advice. Sources: Official Documentation.