performance
Connection Pooling
Reuse a fixed set of physical DB connections — opening new ones per request is too expensive and the DB has a hard limit.
Opening a database connection costs a TCP handshake, TLS negotiation, authentication and session setup — often tens of milliseconds.
A taxi rank of pre-warmed cars vs calling for a new car each time. Way faster, but only if you size the rank correctly.
Key Concepts
1
Paying that on every request is unaffordable. A pool keeps a set of connections open and lends them out.
2
Borrow and return, not open and close. The pool opens its connections at startup. Code borrows one,
uses it, and returns it:
3
try (Connection c = dataSource.getConnection()) { // borrow
...
} // return, not close4
close() here means "give it back". The physical connection stays open for the next caller, and the
expensive setup is paid once and spread across thousands of requests.
close()
5
The pool is also a throttle, and that is half its value. Its maximum size caps how many queries can run
at once. When all connections are busy, the next request waits briefly instead of adding more load.
6
Without that cap, a slow query under traffic means every request opens its own connection, the database
runs out of memory or backend processes, and it stops serving anyone. The pool converts an outage into
queueing.
7
Bigger is not better, and this surprises people. A database executes queries on a limited number of
cores. Give it 500 concurrent connections on 8 cores and it does not do more work — it context-switches
between 500 of them and each one gets slower.
8
8 cores, 100 connections -> each query fights for CPU, all slow
8 cores, 20 connections -> queries run, the rest queue briefly, throughput higher9
A starting point is roughly (cores x 2) + effective spindles, then measure. A pool of 10-20 per instance
is normal, and far more often too large than too small.
(cores x 2) + effective spindles
10
Count instances, not pools. This is the mistake that takes production down. Ten application instances
with a 50-connection pool each want 500 connections; the database allows 200.
11
The sum across every instance — plus batch jobs, plus admin tools — must stay under the server's limit.
Autoscaling makes it worse, because the number of instances changes without anyone revisiting the pool size.
12
The two failures to recognise.Timeout on acquisition means the pool is exhausted. The cause is usually not pool size but slow queries
holding connections longer, or a leak.
13
A leak is a borrowed connection never returned — an early return or a thrown exception on a path
without try-with-resources. The pool shrinks until nothing works, typically hours after deploy. Leak
detection thresholds exist for exactly this.
return
14
What the interviewer is probing.1. "Why pool connections?" Probing: the cost. Stalls: "Convenience." Moves up:
establishing one costs a TCP handshake plus authentication, tens of milliseconds, and each
connection costs server memory.
15
2. "What happens when the pool is exhausted?" Probing: the symptom. Stalls: "It creates
more." Moves up: callers block until one frees or time out — which looks like a slow database when
it is actually a leak or an undersized pool.
16
3. "Is a bigger pool always better?" Probing: the counter-intuitive part. Stalls: "Yes."
Moves up: no — beyond the database's capacity to execute concurrently, more connections add
contention and make throughput worse.
17
4. "Why do serverless functions break pooling?" Probing: the architecture. Stalls: "They do
not." Moves up: each instance has its own pool and they scale independently, so the database sees
hundreds of pools — which is what a proxy solves.