A Postgres pooler that fixes the leak where one client's session bleeds into another
Deploy PgBouncer in transaction mode and a SET you ran on one connection can quietly reappear inside someone else's query. PgDog's pitch: keep the pooler, stop surrendering the Postgres features it silently breaks.
PgDog ships its own SQL parser so it can track SET, LISTEN/NOTIFY and prepared statements per client and restore them before each query, the exact features PgBouncer's transaction mode makes you give up. Kokotov says it's run a year in production at 2M queries/sec.
The thread's war stories agreed the tax is real: one engineer who'd just moved a Django app behind PgBouncer found queryset.iterator()'s server-side cursors didn't survive pooling, and statement_timeout in libpq startup params got silently ignored.
Why "just add a pooler" quietly changes how you use Postgres
PgDog, from PgCat author Lev Kokotov, opens by conceding what every ops team learns the hard way: a connection pooler is a leaky abstraction. Put PgBouncer or RDS Proxy in front of Postgres and "you added a piece of infrastructure that changes the way you use your database." The nastiest change is invisible. Because poolers recycle connections between clients, state set by one client can surface in another's query. In Kokotov's words, "the connection state of one client 'leaks' into the connection state of another."
The trick: a pooler that reads your SQL
PgDog's answer is to stop treating the traffic as opaque bytes. It ships a built-in SQL parser, so it can spot a SET, store that state per client, and restore it through query pipelining before the next statement runs, keeping features like Row Level Security that transaction-mode pooling otherwise forces you to abandon. LISTEN/NOTIFY, another casualty of transaction mode, is rebuilt internally on Tokio broadcast channels plus dedicated connections, so pub/sub survives without losing transactional semantics. Under the hood it's multithreaded rather than sharded across processes, and Kokotov says it's run over a year in production at 2M queries per second.
The migration tax nobody warns you about
The thread filled less with applause than with scar tissue from exactly the leaks PgDog targets. The most concrete came from an engineer who'd moved a Django app behind PgBouncer transaction pooling days earlier:
"the surprise wasn't SET so much as queryset.iterator(). it relies on server side cursors, which don't survive being pooled, so we had to disable it everywhere … also had to move statement_timeout out of the app's connection options into the pooler's own connect query, since libpq startup params just get silently ignored behind it." — an engineer who'd just migrated a Django app
For readers who'd never been bitten, the state leak itself was the shock:
"Wow this is very bad. This actually happens in typical Postgres setups?" — a machine-learning engineer
Even the praise came with an edge case attached
The feature that actually won people over wasn't throughput. It was prepared statements, a wall that had ruled out the obvious alternative:
"The fact that PgDog supports prepared statements is a compelling feature in and of itself. This was a limitation of older versions of pgpool-II thus disqualifying it in efforts where it otherwise could have been beneficial." — an engineer who'd ruled out pgpool-II over exactly that gap
And the pub/sub claim drew the one genuinely skeptical question of the thread:
"Doesn't this NOTIFY performance fix mean that it isn't transactional any more?" — a reader pressing on the transactional guarantee
One detail landed with no caveat at all: PgDog ships under AGPL, welcomed by a commenter tired of BSL relicensing as a relief "instead of the horrible BSL variants that have been going around."
Read together, the thread is the real product spec. Almost nobody argued about speed; they asked whether PgDog survives their specific corner: server-side cursors, schema-per-tenant routing, transactional NOTIFY. That's the tell for where poolers compete now, not on queries per second, but on how many lines of application code you get to delete when you adopt one.
Get breakdowns like this every day
The top Hacker News stories, unpacked — by email, Telegram, or RSS.
His instructions scream 'DO NOT PARSE ANYTHING MANUALLY.' Claude tries anyway
The fix isn't a better prompt. The author wraps the model in Ragel-generated parsers and lets it script its own guardrails in JavaScript, so the next time Claude reaches for manual parsing, deterministic tooling has already done the job.