The connection count climbs faster than our instance classes can keep up. Ops is hot. Every few weeks the same thread resurfaces: we need a pool in front of Postgres before the next scale event.
We move on pgbouncer.
The choice
Two modes on the table. Session pooling hands a connection to a client and gives it back when the client disconnects. Transaction pooling hands one out per transaction. Transaction is tighter – the pool stretches further, the math gets better – but the client loses everything a session holds. Server-side prepared statements. Advisory locks. Temp tables. SET commands that expect to persist.
We pick transaction pooling. Our services do not use advisory locks. Server-side prepared statements we let go. Temp tables are not in our access patterns. The tradeoff is legible: we give up some Postgres ergonomics to gain pool capacity we need more.
It does not land clean.
The footgun
A batch job opens a connection, runs SET statement_timeout, and expects it to stick. Under transaction pooling the SET lives until the end of the current transaction. The next transaction gets the default back. The job’s slow queries are not killed. Nothing crashes. Everything just runs longer than the window allows, and the alert fires on downstream delay, not on the root cause.
It takes two evenings to trace.
The fix is unremarkable – wrap the SET inside the transaction that needs it, not around the connection. Remove the assumption. We add a short note to the onboarding doc: under pgbouncer, SET is transaction-scoped, not connection-scoped. Anyone who grew up on direct Postgres has to learn this once.
The spike that didn’t happen
A week later a feature launch lands with more traffic than we planned for. Under the old topology this would have been the Friday we did not want. Instead the connection count spikes and the pool absorbs it. Postgres sees a flat curve. The feature ships.
The ops channel stays quiet.
That is the moment pgbouncer starts disappearing.
The standup
Three weeks in, nobody brings it up. Chat moves on to other things. A new engineer joins the team and asks what the yellow bar on the infra dashboard means. Pool utilization. Nothing we think about.
Pool capacity is no longer a metric we watch. It is a thing we have. The load graph has a ceiling we built, and we do not mention it.
That is what trivialization looks like. Not the day the tool solves the problem. The week nobody says its name.