CONCEPT Cited by 1 source
Tainted connection¶
Definition¶
A tainted connection is a pooled database connection whose per-session state has been mutated by a caller such that returning it unmodified to the pool would leak that state to unrelated subsequent callers.
The canonical taint source is a SQL SET statement: SET @@session.unique_checks = 0, SET time_zone = '+00:00', SET character_set_client = utf8mb4. These modify a session-level system setting that any subsequent query on the same connection will observe — including queries from a different caller that later borrows the connection from the pool. Other taint sources include open transactions, prepared-statement handles, temporary tables, user-defined variables (SET @x = 1), and session-scoped locks.
Why it matters¶
Every connection pool's correctness invariant is that connections are fungible — any caller can borrow any connection and get the same semantics. A tainted connection breaks that invariant. A pool that returns tainted connections silently changes the behaviour of unrelated callers: query results, commit semantics, encoding, time-zone interpretation all diverge unpredictably.
Once a connection is tainted, the pool has three options:
- Restore the setting on return (expensive, error-prone, not all settings can be cleanly restored — some have no "reset" primitive).
- Close the connection (defeats the pool — every taint costs a new ~50 ms MySQL SSL handshake).
- Don't return it — hold the connection dedicated to the caller's session for the session's duration. Becomes a reserved connection, exits the pool, counts against MySQL's
max_connectionsceiling forever.
Option 3 is the default in Vitess pre-v15 and in PgBouncer session mode. It is safe but pool-bypassing — the number of reserved connections can grow unbounded under workloads that taint frequently, triggering concepts/connection-pool-exhaustion.
Worked example (Vitess)¶
In Vitess's pre-v7.0 era, connections were never tainted because the proxy dropped all client SET statements at the protocol layer (Source: sources/2026-04-21-planetscale-connection-pooling-in-vitess). This preserved pool fungibility but broke ORM compatibility — ORMs issue SET statements at connection open and expect the settings to stick.
In Vitess v7.0 (when the proxy started speaking the MySQL wire protocol for broad ORM compatibility), SET statements were honoured, which meant connections got tainted. Vitess took option 3 — reserve the connection for the session. Subsequent optimisations layered on:
- No-op elision: before tainting, check
select 0 from dual where @@var != value; if theSETis a no-op (setting is already at the desired value), skip reservation entirely. - SET_VAR hint rewriting: for MySQL 8.0 + SET_VAR-eligible variables, rewrite every subsequent query in the session to carry the variable as a query-scoped optimizer hint. The variable applies per query, the connection is never tainted, reservation is avoided.
- Settings pool (v15): instead of avoiding taint, manage it — see patterns/settings-aware-connection-pool.
Relationship to the reservation ladder¶
The taint problem's solutions form a ladder of increasing sophistication:
| Mechanism | Taint handling | Pool cost |
|---|---|---|
| Drop SET statements | Never taint (refuse to honour SETs) | None (but ORM-incompatible) |
| Restore on return | Untaint by resetting to defaults | CPU + round-trip per return |
| Close on taint | Untaint by closing | Full SSL handshake per taint |
| Reserve on taint | Hold tainted connection dedicated to session | Pool bypass; MySQL max_connections pressure |
| SET_VAR rewrite (per-query hint) | Avoid taint by applying setting per query | Per-query hint-serialisation cost |
| Settings-aware pool | Index pool by settings profile; match on borrow | Bookkeeping + occasional setting-application on borrow |
The Vitess trajectory climbed this ladder from row 1 (pre-v7) → row 4 (v7) → rows 4+5 (mitigated v7–v14) → row 6 (v15 settings pool).
Not unique to MySQL¶
The problem is general to any session-stateful database. PostgreSQL has analogous session state (SET search_path, SET timezone, prepared statements, advisory locks, SET ROLE) and PgBouncer documents three pooling modes expressing different taint-tolerance stances:
- Session pooling — one connection per client for the session's lifetime. Taint-tolerant because the pool doesn't rotate connections across callers. Equivalent to reservation for the whole session.
- Transaction pooling — connection returned to the pool at transaction boundary. Breaks if callers rely on between-transaction session state (prepared statements,
SET, temp tables). - Statement pooling — connection returned after each statement. Breaks on any multi-statement session state, including transactions.
PgBouncer's modes are just named points on the taint-tolerance axis; Vitess's settings pool is a more sophisticated point on the same axis (keep pool rotation, but index by taint profile).
Seen in¶
- sources/2026-04-21-planetscale-connection-pooling-in-vitess — canonical first wiki framing of the tainted-connection problem as the structural driver of Vitess's three-era connection-pooling-design evolution. Harshit Gangal (Vitess core maintainer): "when using connection pooling, all connections in the pool share the same settings. Any modifications made to a connection will render it unusable for other requests, as it becomes 'tainted'. Therefore, either settings changes made to a connection must be restored to their original values or the connection should be closed after the operation is complete to ensure the stability of the connection pool." First wiki citation of the "tainted" vocabulary and the three-option decision structure (restore / close / reserve).
Related¶
- concepts/reserved-connection — the pool-bypass answer to taint.
- concepts/session-level-system-setting — the dominant taint source in practice.
- concepts/set-var-hint — the per-query escape hatch that avoids taint.
- concepts/connection-pool-exhaustion — what happens when reserved connections accumulate past MySQL's ceiling.
- patterns/settings-aware-connection-pool — the structural fix (index the pool by taint profile).
- patterns/connection-multiplexing-proxy — the architectural envelope within which taint management happens.
- systems/vitess — canonical production-scale taint-management implementation.
- systems/pgbouncer — the Postgres analogue, with three named pooling modes.