Connection Pooling
A cache of database connections maintained so that connections can be reused when future requests to the database are required.
Detailed Explanation
Creating a new database connection for every incoming API request is slow and resource-heavy. A connection pool keeps a set of 'warm' connections open. When a request comes in, it 'borrows' a connection, uses it, and 'returns' it to the pool. This drastically improves the scalability of database-backed applications.
Quick Summary
A connection pool reuses a small set of warm database connections across many requests. Without one, your app exhausts the database's connection limit and stalls; with one, it scales smoothly.
Key Takeaways
- Opening a Postgres connection costs ~5-20ms and ~10MB on the server, far too expensive per request.
- App-level pooling (HikariCP, pg-pool, SQLAlchemy) is fine for traditional servers; external poolers (PgBouncer, RDS Proxy) help for serverless and high-concurrency.
- Pool size ≠ "as large as possible." The DB has its own connection cap; oversizing causes contention and OOMs.
- Serverless functions need an external pooler, each instance otherwise opens its own connections and overwhelms the DB.
- Set sane timeouts (acquire, idle, max-lifetime) so leaked or stale connections recycle.
When to use it
- Any web server or worker that talks to a relational database.
- Serverless apps (Lambda, Vercel, Cloud Run) using PgBouncer or Neon's serverless driver.
- High-concurrency read replicas where many short queries hit the DB.
- Background job workers that would otherwise open a connection per task.
Common Mistakes
- Setting pool size too high; 1000 app connections × N instances overwhelms even big databases.
- Holding connections across slow external calls (HTTP, file I/O), starving the pool.
- Forgetting to release connections in error paths, leaks build up until the pool is exhausted.
- Using session-level features (e.g., session variables, prepared statements) with a transaction-mode pooler like PgBouncer.
Connection Pooling, Frequently Asked
How large should my pool be?
Start small: roughly (cores × 2) + spindles is a classic heuristic, but most apps are happy with 10–20 connections per app instance. Tune by watching DB-side wait events; bigger isn't faster past a surprisingly low threshold.
Why is PgBouncer needed for serverless?
Each serverless instance spawns its own short-lived process. Without a pooler, you can have hundreds of instances each opening connections, instantly hitting Postgres's `max_connections`. PgBouncer (or a managed equivalent) multiplexes them onto a smaller pool of real DB connections.