PgBouncer
"Lightweight connection pooler for PostgreSQL." C, ISC licence, 4,330★ / 591 forks, repository created 2010-05-04 and pushed 2026-08-19. The copyright line dates it further back than the repo does — "Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ" — so this is nineteen-year-old infrastructure that shipped a commit yesterday.
Read from pgbouncer.org and the repository (T1), pulled from awesome-postgres' Utilities section to close a hole this spoke opened itself. postgresql records that a connection is an OS process — "it starts ('forks') a new process for each connection" — and then asserts that this "is why connection pooling is mandatory at scale" without a single source for what pooling is. This page is that source, and the interesting part is not that pooling exists but what it costs.
Three modes, and each one takes something away
- Session pooling — "Most polite method. When a client connects, a server connection will be assigned to it for the whole duration it stays connected."
- Transaction pooling — "A server connection is assigned to a client only during a transaction. When PgBouncer notices that the transaction is over, the server will be put back into the pool."
- Statement pooling — "Most aggressive method. This is transaction pooling with a twist: Multi-statement transactions are disallowed."
Session pooling saves the fork, not much else: 500 idle clients still pin 500 backends. Transaction pooling is the one that produces the dramatic ratios people quote, because a pool of a few dozen backends can serve thousands of clients that spend most of their time between transactions. And it is the one the docs warn about in their own words: "This mode breaks a few session-based features of PostgreSQL. You can use it only when the application cooperates by not using features that break."
The list of what breaks is the actual content
Under transaction pooling, a client is not talking to a backend; it is talking to whichever backend is free this transaction. So everything Postgres scopes to a session stops being reliable. The documented casualties:
SET / RESET · LISTEN · WITH HOLD CURSOR · PREPARE / DEALLOCATE ·
PRESERVE/DELETE ROWS temp tables · LOAD · session-level advisory locks
Read that as a definition rather than a caveat: transaction pooling is the decision to give up
session state in exchange for connection density. Two of the entries matter more than the rest for
this spoke's other pages. LISTEN is half of LISTEN/NOTIFY, the mechanism several of
awesome-postgres' Work Queues section is built on — so a deployment can lose the event channel it
was relying on by adding a pooler in front, and pgmq's decision to poll a table with
SKIP LOCKED instead of listening is, among other things, immune to this. And prepared statements
being unavailable is why so much client-library documentation carries a "if you use PgBouncer"
paragraph.
Why it belongs here
It is the operational consequence of a design fact this spoke already paged. postgresql's process-per-connection model is the reason the pooler exists; mysql's thread model makes the same tool less urgent. Placed against the spoke's axis, PgBouncer is a proxy that changes nothing about where data lives — unlike vitess' VTGate, which is also a proxy speaking the wire protocol but exists to route across shards. Same architectural position, entirely different job: one multiplexes clients onto scarce backends, the other multiplexes a query onto many databases. The corpus now has both, which makes "put a proxy in front of it" legible as a family of answers rather than one trick.
Not covered here. The failover story (PgBouncer plus patroni is the standard pairing, and how the pooler learns the primary moved is unsourced on this page), pool sizing, prepared-statement support in later versions, and any measurement at all — every claim above is a capability statement from the project's own feature page, with no benchmark attached.
Related
postgresql · patroni · pgmq · vitess · awesome-postgres · online-transaction-processing · operational-databases