PATTERN Cited by 4 sources
Connection multiplexing proxy¶
What it is¶
A connection multiplexing proxy is a server tier placed between application clients and a database that:
- Accepts a large number of client-facing connections (thousands to millions).
- Owns a much smaller, bounded pool of upstream connections to the backing database.
- Multiplexes incoming client queries onto the smaller upstream pool, queueing excess requests when the pool is saturated.
The client sees "the database" — native wire protocol, normal connection semantics. The database sees "one proxy" — a bounded, well-behaved consumer of a fixed connection budget.
Canonical instances: Vitess VTTablet (for MySQL), PgBouncer (for Postgres), ProxySQL, RDS Proxy, Aurora Proxy.
Problem¶
Each open database connection carries non-trivial cost:
- Memory: 5–10 MB per PostgreSQL backend process; ~256 KB–1 MB per MySQL thread plus per-query buffers (Source: sources/2026-04-21-planetscale-scaling-postgres-connections-with-pgbouncer).
- File descriptors: one per TCP socket.
- Scheduler overhead: more processes/threads → more context switches.
- Per-connection handshake (~50 ms for MySQL over SSL — the direct latency cost application-level pooling eliminates; Source: sources/2026-04-21-planetscale-connection-pooling-in-vitess).
This makes the database's max_connections a memory-derived
hard ceiling, not a tunable knob. RDS MySQL
caps at 16,000. Raising it risks memory
overcommit → OOM crash.
Meanwhile, N application processes each hold their own local
pool of P connections. Aggregate upstream connection count =
N × P. Scale-out of application tier (especially serverless,
where every invocation is a fresh process with fresh pool)
trivially breaches the database ceiling. Nothing at the
application tier can prevent this — each process sizes its own
pool independently, and no process sees the aggregate.
Solution shape¶
Place a single logical tier between applications and the database that owns the upstream connection budget. Applications connect to the proxy, not the database. The proxy enforces the ceiling once, globally, regardless of how many application processes connect.
┌─────────┐ ┌─────────┐ ... ┌─────────┐
│ app 1 │ │ app 2 │ │ app N │ N * P connections here
└────┬────┘ └────┬────┘ └────┬────┘ (cheap: gRPC/TLS to proxy,
│ │ │ or even plain TCP)
└───────────┴───────────────┘
│
┌──────┴──────┐
│ proxy │ ← owns the upstream ceiling
│ (bounded │ (e.g. 300 connections)
│ pool) │
└──────┬──────┘
│ 300 MySQL connections max
▼
┌─────────────┐
│ database │
└─────────────┘
The client-facing ceiling is decoupled from the upstream
ceiling: a PlanetScale MySQL database sustains 1,000,000
concurrent client connections (Liz van Dijk benchmark, Source:
sources/2026-04-21-planetscale-one-million-connections)
against backing MySQL instances with max_connections in the
hundreds.
Why the proxy can multiplex¶
Most OLTP queries are short (sub-ms to tens of ms). A single
upstream connection can serve hundreds or thousands of client
queries per second. The proxy's pool size just needs to be
concurrency × average-query-duration / 1 sec — typically orders
of magnitude smaller than the client-facing connection count.
Admission control falls out naturally: when every upstream connection is in use, new queries queue at the proxy (pool-exhaustion becomes a signal with a natural threshold), and the proxy can reject, wait, or apply backpressure without overwhelming the database.
Consequences¶
Positive:
- Global admission control: upstream ceiling is enforced once, regardless of client topology.
- Cold-start friendly: clients connect cheaply to the proxy (no MySQL SSL handshake per fresh process).
max_connectionsstability: the ceiling is a property of the proxy's config, not an emergent property of client scale.- Observability choke point: the proxy is a natural place for query logging, rewrite, caching, routing, throttling (patterns/query-routing-proxy-with-health-aware-pool).
- Composable with two-tier pooling — the proxy is the second tier beneath the application's own local pool.
Negative:
- One more hop on the query path (latency cost: typically <1 ms on same-network proxy).
- Session-state handling becomes harder — tainted connections can't be rotated across callers without mitigation (see patterns/settings-aware-connection-pool and concepts/reserved-connection).
- Proxy availability becomes a new dependency — the proxy's HA story must match or exceed the database's.
- Feature gap — some database features (LISTEN/NOTIFY in Postgres; prepared statements across callers; session advisory locks) are incompatible with aggressive pooling modes.
Known implementations¶
| Proxy | Backing DB | Pooling granularity | Distinctive features |
|---|---|---|---|
| Vitess VTTablet | MySQL | Per-query (+ settings-pool) | Lockless atomic pool; in-cluster sidecar per MySQL instance; shard-aware routing; settings-aware pool from v15 |
| PgBouncer | Postgres | Session / transaction / statement | Lightweight (single-threaded event loop); query_wait_timeout admission control; canonical three-mode exposition (Source: sources/2026-04-21-planetscale-scaling-postgres-connections-with-pgbouncer) |
| ProxySQL | MySQL | Connection multiplexing | Query routing, caching, firewall |
| RDS Proxy | MySQL / Postgres | Per-transaction | AWS-managed; IAM auth integration |
| PgCat | Postgres | Session / transaction | Rust-implemented; sharding support |
The shape is general: applications-to-many, proxy-to-few.
Relationship to other patterns¶
- patterns/two-tier-connection-pooling — the composition pattern that pairs the app-tier local pool with this multiplexing proxy tier.
- patterns/query-routing-proxy-with-health-aware-pool — the VTTablet instance extended with routing and health awareness.
- patterns/settings-aware-connection-pool — the v15 Vitess refinement that keeps the proxy's pool benefits through session-setting mutations.
- patterns/consolidate-identical-inflight-queries — a sibling proxy-tier primitive that dedupes identical concurrent reads onto a single upstream execution.
Seen in¶
-
sources/2026-04-21-planetscale-connection-pooling-in-vitess — canonical wiki first citation. Harshit Gangal (Vitess core maintainer, PlanetScale, 2023-03-27) frames Vitess's central architectural answer to unbounded application-tier connection growth: "By allowing client applications to connect only to Vttablet, the need for a connection pool at the application level was eliminated. This meant that connections could be centrally managed in Vttablet, with the maximum number of allowed connections being configurable in Vttablet, rather than growing unbounded as the number of applications increased." The VTTablet tier is the canonical 2010 YouTube-origin instance of the pattern; the 2023 post is the load-bearing wiki disclosure.
-
sources/2026-04-21-planetscale-one-million-connections — Liz van Dijk benchmarks the client-facing ceiling the multiplexing-proxy pattern enables: 1,000,000 concurrent client connections against bounded upstream MySQL pools, 62.5× above RDS MySQL's 16,000-connection ceiling.
-
sources/2026-04-21-planetscale-scaling-postgres-connections-with-pgbouncer — Ben Dicken canonicalises the Postgres analogue: PgBouncer as the multiplexing-proxy tier between clients and Postgres, with explicit
max_client_conn→default_pool_size→max_db_connections→max_connectionsconfiguration chain. -
sources/2025-09-24-planetscale-processes-and-threads — Ben Dicken canonicalises the OS-substrate why: "It maintains its own pool of direct connections to the database, typically between 5 and 50. … It acts as a funnel: pushing the queries from thousands of connections into tens of connections."
Related¶
- patterns/two-tier-connection-pooling
- patterns/settings-aware-connection-pool
- patterns/query-routing-proxy-with-health-aware-pool
- concepts/connection-pool-exhaustion
- concepts/max-connections-ceiling
- concepts/memory-overcommit-risk
- concepts/tainted-connection
- concepts/reserved-connection
- systems/vitess
- systems/vttablet
- systems/pgbouncer
- systems/mysql
- systems/postgresql