figma

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.