> ## Content Index
> Fetch the complete content index at: https://debugly.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# What a Connection Pool Is For, and the Setting That Ruins It
- URL: https://debugly.dev/connection-pool-size-myth/
- Published: 2026-10-09T16:55:00.000Z
- Updated: 2026-10-17T05:55:45.000Z
- Description: The pool was raised to two hundred to fix the timeouts, and the database got slower, because the connections were the contention rather than the cure, and…
- Author: Rohit Bhadani
- Tags: Deep Dive, Database, Performance

Start at the timeouts and you miss it. Start at the incident and you miss how it arrived. The bug lived in the hand-off.

This is the anatomy of the oversized pool, and of the specific property that makes the intuition wrong: the database has a finite number of cores, so the connections beyond the core count do not add throughput, they add scheduling overhead and lock contention.

This was Postgres behind an application pool, and the mechanics are the same for any database with a per-connection process or thread.

## What the pool is actually for

The pool is the reuse, and the reuse is the connection's cost, and the cost is the TCP and the authentication and the setup, and the setup is the milliseconds, and the milliseconds is the per query, and the per query is the expensive, and the expensive is the pool's reason, and the reason is the amortisation, and the amortisation is the kept connection, and the kept is the saving.

The pool is the not the concurrency's increase, and the not is the misunderstanding, and the misunderstanding is the size's inflation, and the inflation is the two hundred, and the two hundred is the contention, and the contention is the slower, and the slower is the incident, and the incident is the reason the pool's purpose should be understood before its size is changed.

The right size is the cores times the small factor, and the factor is the two or the three, and the three is the saturation, and the saturation is the maximum throughput, and the maximum is the not the two hundred, and the not two hundred is the smaller pool, and the smaller is the faster, and the faster is the counterintuitive, and the counterintuitive is the fix.

## Why the big pool is slower

The database is the cores, and the cores is the eight, and the eight is the parallel work, and the parallel work is the eight queries, and the eight queries is the throughput's ceiling, and the ceiling is the not raised by the connections, and the not raised is the two hundred's futility, and the futility is the overhead.

The overhead is the context switching, and the switching is the two hundred processes, and the processes is the scheduler's work, and the work is the CPU's waste, and the waste is the query's slowdown, and the slowdown is the throughput's drop, and the drop is the incident, and the incident is the bigger pool's cost.

The overhead is also the lock contention, and the contention is the more concurrent transactions, and the transactions is the more waiting, and the waiting is the lock's queue, and the queue is the latency, and the latency is the timeout, and the timeout is the original complaint, and the complaint is the worse, and the worse is the fix's target, and the target is the smaller pool.

This is the same contention as [the idle transaction that held the lock for six hours](https://debugly.dev/idle-in-transaction-held-the-lock/), which is why the two fixes look alike.

## The fixes

**Sized the pool to the cores times a small factor.** The pool became the cores times two, and the two was the saturation, and the saturation was the maximum throughput, and the maximum was the faster, and the faster was the fix, and the fix was the smaller number, and the smaller was the counterintuitive, and the counterintuitive was the discipline.

**Put a pooler in front of the application pool.** The pooler is the PgBouncer, and the PgBouncer is the transaction mode, and the transaction mode is the few database connections, and the few is the database's relief, and the relief is the fix, and the fix is the layer, and the layer is the discipline, because the application instances multiply the pool and the multiplication is the database's load.

**Measured the saturation point.** The throughput against the pool size is the curve, and the curve is the peak, and the peak is the right size, and the right size is the fix, and the fix is the measurement, and the measurement is the discipline, because the guessed size is the wrong size.

**Watched the waiting rather than the connections.** The waiting is the real queue, and the queue is the saturation, and the saturation is the metric, and the metric is the fix's target, and the target is the alert, and the alert is the discipline, because the connection count is the not the throughput.

**Checked the query duration before the pool.** The slow query is the connection's hold, and the hold is the pool's exhaustion, and the exhaustion is the timeout, and the timeout is the complaint, and the complaint is the query's fault, and the fault is the fix, and the fix is the index, and the index is the discipline, because the pool is the symptom's place and the query is the cause.

## The takeaway

A connection pool exists to amortise the connection setup cost, not to increase concurrency, and a pool larger than the database's core count adds context switching and lock contention while reducing throughput. Size it to the cores times a small factor, put a transaction-mode pooler in front, measure the saturation curve, and fix the slow query before resizing the pool.

The timeouts were fixed by making the pool smaller, because two hundred connections on an eight-core database spent their time context switching and waiting on locks rather than running queries. The lesson I keep is the property, not the incident.

---

Published on [debugly.dev](https://debugly.dev/) by [Rohit Bhadani](https://debugly.dev/about/) | Founder of [Krova Cloud](https://krova.cloud/?ref=debugly.dev)