Skip to content

SYSTEM Cited by 2 sources

PgBouncer

What it is

PgBouncer (pgbouncer.org) is a lightweight open-source connection pooler for Postgres, widely deployed in production Postgres environments to reduce the cost of short-lived client connections. It speaks the Postgres wire protocol on both sides — clients connect to PgBouncer as if it were Postgres, and PgBouncer multiplexes their queries across a pool of real server-side Postgres connections.

Three pooling modes:

  • Session pooling — client holds the server connection for the life of its client-side connection; most transparent, least resource savings.
  • Transaction pooling — server connection released back to the pool at COMMIT / ROLLBACK; most common production default; breaks features that rely on session state (prepared statements, SET, temp tables, cursors).
  • Statement pooling — server connection released after each statement; incompatible with multi-statement transactions.

Transaction pooling is the standard production trade-off — 10-100× more clients per server connection, with the prepared-statement / session-state breakage handled via client-side workarounds (protocol-level PREPARE rewrites, SET LOCAL migrations, etc.).

Seen in

  • Canonical wiki framing of connection pooling as the primary RSS-reduction lever for Postgres. Simeon Griggs (PlanetScale, 2026-03-30) writes verbatim: "Efficient connection pooling can be the best way to reduce RSS usage. Fewer active connections result in fewer copies of all that per-process overhead." Companion piece to the sibling Dicken 2026-04-21 configuration-hierarchy post: Dicken documents the connection-count reduction itself (the mechanism); Griggs names the downstream RSS reduction (the memory consequence). Reinforces transaction-mode pooling as the default PlanetScale PgBouncer configuration — transaction-returned connections cut the backend-count multiplier across work_mem allocations, catalog caches, and cached plan / prepared-statement state. Implicitly framed as a cheaper RSS-reduction lever than tuning work_mem — the post's operational discipline rules explicitly disclaim work_mem tuning as a default change.

  • sources/2026-04-21-planetscale-scaling-postgres-connections-with-pgbouncercanonical wiki disclosure of the PgBouncer configuration surface. Ben Dicken (PlanetScale, 2026-03-13) walks the full connection chainmax_client_conndefault_pool_sizemax_db_connections/max_user_connections → Postgres-side max_connections + superuser_reserved_connections — and formalises the sizing arithmetic as num_pools × default_pool_size ≤ max_db_connections ≤ max_connections − reserved_slack. Three canonical PlanetScale deployment topologies (concepts/pgbouncer-deployment-topology): (1) local PgBouncer on the same server as the primary (port 6432, same credentials, shares failure domain with primary); (2) dedicated primary PgBouncer on separate nodes, "client connections persist through resizes, upgrades, and most failovers" — invoked by appending |your-pgbouncer-name to username, stitches through the local bouncer; (3) dedicated replica PgBouncer routing to replicas, bypassing the local bouncer. Three pooling modes re-canonicalised with Dicken's unequivocal verdict: "Transaction pooling is the only sensible option" — session pooling is 1:1 and "does little to reduce Postgres connection count"; statement pooling "disallows multi-statement transactions entirely. Most apps need this, so not useful in 99% of cases!"; transaction pooling is the default with the known unsupported-features fallback (LISTEN, session-level SET/RESET, SQL PREPARE/DEALLOCATE). PlanetScale only supports transaction pooling. Canonical query_wait_timeout default: 120 seconds, queue-then-disconnect semantics. Canonical 5+ MB RAM per Postgres connection datum — specific number behind the general "memory overhead" framing in Dicken's sister 2025-09-24 Processes and Threads post. Three worked sizing scenarios (PS-80 / M-2650 / M-1280) instantiate the three-pool-size budget allocation pattern with operator-facing numbers; the single-tenant-per-DB M-1280 scenario inverts the typical multi-tenant sizing intuition, making default_pool_size (not max_user_connections) the load-bearing dial. Two canonical multi-PgBouncer patterns: layered PgBouncer (app-side funnel + DB-side funnel) and isolated PgBouncer per workload (independent PgBouncer per web/worker/analytics class). Dicken's compose-with-Traffic-Control framing: "PgBouncer manages connections, Traffic Control manages resource consumption. The two approaches complement each other well." Neki referenced as future-generation Postgres- scaling substrate: "Though there are upcoming systems like Neki which will solve this problem in a more robust way, PgBouncer has proven itself an excellent connection pooler for Postgres."

  • — PgBouncer named as a component of PlanetScale's proprietary proxy layer for PlanetScale for Postgres: "connection pooling via our proprietary proxy layer, which includes PgBouncer for connection pooling." PgBouncer is the pooling engine inside the proprietary proxy; the proxy also adds query buffering and automatic-failover integration that PgBouncer alone doesn't provide. First canonical wiki instance of PgBouncer-as-pooling-engine-inside-vendor- proxy. — canonical wiki account of PgBouncer on Kubernetes and the latency implications thereof. Alexander Kukushkin (Zalando, 2020-06-23) establishes: (1) PgBouncer is the Zalando Postgres Operator 1.5 default pooler, chosen over Pgpool-II / Odyssey / pgagroal for maturity and simplicity; (2) so_reuseport (so reuseport pgbouncer scaling) is the canonical PgBouncer multi-core scaling primitive — "essentially a way to get PgBouncer to use more CPU cores"; (3) hyperthread placement matters — two PgBouncer instances on sibling HTs of a single physical core show ~2× the latency of one PgBouncer on an isolated physical core, traceable via perf tracepoints to softirq NET_RX / NET_TX contention; (4) the Kubernetes CPU Manager static policy is the fix; (5) iptables kube-proxy produces visibly non-uniform load across PgBouncer pods (977/995/585/993 m CPU cores across four pods); (6) Zalando's default operator topology is the

AZ-spread Deployment + Service, with the big pooler affinity + small pooler HA pattern as the documented escape hatch for latency-sensitive workloads. First PgBouncer-on-Kubernetes source on the wiki — complements the PlanetScale altitude (dedicated-server Postgres, non-Kubernetes operational model) with the cloud-native / K8s-operator view.

Caveats

  • PgBouncer is a single-process daemon by default. Multi-instance deployments need external coordination; HA + scaling of PgBouncer itself is an operator responsibility.
  • Prepared statements + transaction pooling is an ongoing ecosystem friction point. Postgres 14 added protocol-level prepared-statement support in PgBouncer (via pg_type caching mode), but older versions + non-standard clients still break.
  • Alternative poolers existpgcat (Rust, multi- threaded, sharding-aware), Odyssey (Yandex, TLS-terminating pooler), Supavisor (Supabase, Elixir). PgBouncer remains the canonical default.
Last updated · 542 distilled / 1,571 read