Dev Learning

Connection pooling

Reuse a small set of open database connections, instead of opening one per request.

1

What problem does connection pooling solve?

Opening a database connection is expensive. There is a TCP handshake, TLS, a login, and on some databases a whole new process. Doing that for every request can take longer than the query itself.

Worse, databases have a hard limit on how many connections they allow at once. Go past it and new connections are refused, which looks like a total outage even though the database is barely doing any work.

2

What connection pooling costs you

What it costs you
The pool is a queue, so when it is full, requests wait. A pool that is too small becomes your bottleneck while the database sits idle. Pools also carry state. A connection handed back mid-transaction, or with a session setting still on, causes bugs that show up in unrelated requests later.

Every concept in here states a cost. A technique with no price is a technique you have not understood yet — and naming the cost out loud is most of what separates a senior answer from a junior one.

3

When to reach for connection pooling, and when not to

Use it when

  • Always, for any service that talks to a relational database. This is a default, not a decision.
  • Especially when your application servers scale up and down. Each new server multiplies your connection count.

Do not, when

  • Serverless functions that scale to hundreds of instances, each with its own pool. There you need a separate pooler sitting between them and the database, or you will hit the limit quickly.
4

How connection pooling actually works

few, reusedApp serverpool of 20App serverpool of 20PoolerpgbouncerDatabasemax 100 connswithout the pooler, connections multiply by server count
Twenty app servers with a pool of 20 each is 400 connections. A default Postgres allows 100. Do the multiplication before you deploy.

Sizing is the part worth knowing. Your instinct says a big pool. The right answer is usually a small one. A database with 8 cores cannot truly do more than a couple of dozen things at once. So 500 connections do not run side by side. They queue inside the database, where you cannot see them, and every query gets slower together. A common starting point is a few times the core count. Then measure.

The failure has a recognisable shape. Response times climb on every endpoint at once. Database CPU is not high. Errors say "timed out waiting for connection". That is pool exhaustion. The usual cause is slow queries holding connections, rather than too much traffic. One endpoint running a 10-second report can starve the whole service.

Set a timeout on getting a connection. Waiting forever turns a slow dependency into an endless pile of stuck requests. Failing fast lets you drop traffic and stay up.

For serverless, or for a very large number of instances, put a dedicated pooler in front. In transaction mode it lends a real connection to a request only for the length of one transaction, so hundreds of clients share a handful of connections. The cost is that anything spanning transactions breaks, including session-level settings and some prepared-statement behaviour.

Common trap
  • Fixing timeouts by making the pool bigger. That usually moves the queue into the database and makes it worse.
  • Holding a database connection while calling an external API. Never do work you do not control while holding something scarce.
  • Forgetting that pool size multiplies by the number of application instances.
Say this
"Pooled connections, sized at roughly four times the database core count, with a two-second timeout on getting one so we fail fast instead of piling up. With autoscaling I would put pgbouncer in front, so total connections stay flat no matter how many app servers we run."
5

The connection pooling follow-up an interviewer will ask

"You added more application servers and the database got slower. Why?"

What a strong answer sounds like

Because each new server brought its own pool. Total connections went up, while the database's ability to do work at the same time stayed the same. Past a point, more connections means more fighting inside the database: context switching, waiting on locks, memory per connection. So everything slows down together. Adding capacity made things worse, which is why it is a good interview question. The fix is a pooler in front, so the database sees a fixed small number of connections no matter how many app servers exist, and the waiting happens somewhere you can see and control. The cost: the pooler is another hop and another thing to run, and transaction-mode pooling quietly breaks session-scoped features.

6

Your turn

60-second self-check

Your service times out under load, but database CPU is only 30%. What is the most likely cause?

Stuck? Take one hint at a time0 of 2 used
0 / 25 characters
7

Learn it from the source

This page gets you interview-ready. These take you to where the idea is actually specified or measured — read them when you want the real depth.

Where you will use this