PGKeeper: Building the Bouncer We Needed for Postgres | Figma Blog (opens in new tab)
Figma built PGKeeper to replace PgBouncer as its PostgreSQL connection and load-management layer. Growing traffic, sharding, and stricter reliability requirements exposed PgBouncer’s limits in scalability, prioritization, backpressure, connection protection, and extensibility. PGKeeper is a custom Go service positioned between Figma’s DBProxy routing layer and PostgreSQL, designed to protect databases from overload and connection churn.
Figma’s Database Architecture
- PostgreSQL powers Figma’s OLTP workloads.
- Figma scales through horizontal and vertical sharding across multiple database instances.
- DBProxy hides sharding complexity from application code by:
- Parsing and analyzing queries.
- Selecting the appropriate PostgreSQL instances.
- Rewriting requests into queries for the selected targets.
- A dedicated set of connection-pooler replicas serves each PostgreSQL machine, creating an n-to-one relationship between poolers and databases.
Why PgBouncer Was No Longer Enough
Limited scalability
- PgBouncer’s single-threaded architecture created a vertical scaling ceiling.
- Adding replicas helped, but uneven load distribution caused performance degradation.
Insufficient load management
- PgBouncer could not prioritize critical traffic over lower-priority or misbehaving requests.
- It lacked effective backpressure and advanced load-shedding algorithms such as Controlled Delay (CoDel).
- CoDel sheds work based on how long requests wait, rather than simply counting queued requests.
Unsafe connection behavior
- PostgreSQL connections are expensive resources.
- Rapid connection creation and churn could destabilize database nodes.
- Recovery after overload could trigger another surge of connections, creating cascading failures and prolonged overload.
Limited extensibility and control
- Figma needed deep observability, feature-flagged rollouts, admission control, and fair resource sharing.
- Even maintaining small PgBouncer patches proved costly.
- Extending PgBouncer substantially would create an ongoing maintenance burden.
Why Connection Pooling Could Not Live in DBProxy
- Figma generally limits each PostgreSQL instance to roughly 100 pooled connections.
- Hundreds of stateless DBProxy replicas sit in front of those databases.
- Giving every DBProxy replica its own pool would either exceed database connection limits or require complex coordination.
- Centralizing pooling in a separate service provided a better fit for the mismatch between many routers and a small fixed connection budget.
Why Figma Built PGKeeper
- PGCat addressed PgBouncer’s single-threaded scalability problem, but customizing it would require deep changes to its core execution paths.
- Those changes would likely require Figma to maintain a long-term fork.
- Figma therefore created PGKeeper as a Go-based service tailored to its infrastructure and operational requirements.
- Its role is to act like a goalkeeper: protecting PostgreSQL from harmful traffic and protecting connections from uncontrolled churn.
PGKeeper was chosen because Figma needed more than a basic connection pooler: it needed a scalable, observable, controllable layer capable of prioritizing traffic and preventing database overload.