← All scenarios

Scenario · Connections & Pooling

Pool size misconfiguration

A sandboxed PostgreSQL incident — investigate with your own tools, submit a fix, and get deterministic Detect / Fix / Trap scoring.

L3 · 10–15 min · runs locally in Docker

Launch

Start this scenario

Boot it in a real PostgreSQL sandbox and investigate with psql, EXPLAIN and pg_stat_statements.

ride postgres start stage-03/09-pool-size-misconfiguration

Part of these paths

Show the postmortem & investigation hints spoilers
Pool size misconfiguration
Type: incident simulation · Topic: Connections & Pooling · Level: L3 · Duration: 10–15 min
Launch: ride postgres start stage-03/09-pool-size-misconfiguration

POSTMORTEM (root cause · how it was found · the fix · lesson)
Root cause: PgBouncer's default_pool_size was set far too small for the offered
load. PostgreSQL had plenty of free connection slots and answered direct queries
instantly, but the pooler only ever opened a couple of server connections, so
clients queued behind them (cl_waiting > 0). The throughput ceiling was the
pool's configuration, not the database.

How it was found: pg_stat_activity was far from max_connections and direct
queries were fast; PgBouncer's SHOW POOLS showed clients waiting on a tiny pool
and SHOW CONFIG showed an undersized default_pool_size.

The mitigation: the durable fix is to raise default_pool_size and reload
PgBouncer; to clear the immediate queue, free the busy server connections so
waiters are promoted (cl_waiting → 0).

Lesson: distinguish a Postgres capacity problem from a pooler sizing problem —
"Postgres has headroom but the app is throttled" points at default_pool_size.
Size the pool to the workload; don't raise Postgres max_connections (wrong
layer) or add indexes.

INVESTIGATION HINTS (the staged path to diagnose and fix)
1. The app is throttled but PostgreSQL is nowhere near max_connections and direct queries are fast. The bottleneck is PgBouncer's pool size, not the database. On the admin console: SHOW POOLS (cl_waiting > 0, every server slot busy) and SHOW CONFIG (default_pool_size is tiny).
2. Confirm Postgres has capacity: SELECT count(*) FROM pg_stat_activity; SHOW max_connections; — plenty of headroom. The pool is artificially small for the offered load.
3. The durable fix is to raise default_pool_size in PgBouncer and reload. To relieve the incident now, free the busy server connections (terminate the pool_client backends) so the queue drains; cl_waiting drops to 0. Do NOT raise Postgres max_connections — that is the wrong layer.