Vitess, the MySQL proxy layer built at YouTube, consolidates reads - if an identical query is already in flight on a tablet, the second caller waits and receives the first query's result rather than issuing its own. What does that buy, what does it break, and which workloads should switch it off?
Show the full answer Hide the answer
The situation it was built for
A popular video's row is read by every viewer at once. Those reads are byte-for-byte identical, they return the same bytes, and a conventional proxy sends every one of them to the database. At 20,000 reads per second of one row, 19,999 of them are redundant work the storage engine still has to do.
Vitess answers this inside the tablet server. Its documentation describes the behaviour precisely: when a query arrives and an identical query is already executing, the arriving query waits and is handed the first one's result. Identical means byte-for-byte, including literals and bind variables, so this is deduplication of exact repeats, not a cache.
What it buys, mechanically
The work the database performs falls to one execution per distinct query per in-flight window, where the window is the duration of that query. A slow query consolidates more than a fast one, which is the useful inversion: the worse the hot-row problem gets, the longer each execution takes, so the more callers each execution serves. The feedback loop runs the safe direction.
It is also cheap to reason about, because nothing is stored. There is no TTL, no invalidation, no memory budget for keys, and no stale read beyond the lifetime of a single in-flight query.
What it breaks
Read-after-write consistency. The Vitess docs give the exact failure: user A writes, user B reads, B's read is consolidated onto a query that started before A's commit, and B sees the old value. Vitess therefore supports restricting consolidation to replicas (--enable-consolidator-replicas), and the documented advice for a primary that serves read-your-writes traffic is to turn it off there.
The second hazard is the waiter queue. Every waiter holds a connection and a goroutine while the one execution runs, so a 9-second query with 8,000 waiters is 8,000 occupied callers, not a saved 8,000 queries. Vitess exposes --consolidator-query-waiter-cap, with no limit by default, and a newer option to reject over-cap queries with RESOURCE_EXHAUSTED rather than let them execute independently. Leaving the cap unset converts a slow query into an unbounded memory and connection pile-up.
Where copying this is a mistake
Consolidation only fires on exact repeats. Add user_id to the predicate, or a cursor, or a timestamp, and the hit rate goes to zero while the machinery stays. It is the right tool for a small number of very hot, parameter-free reads and the wrong tool for a workload of mostly distinct queries, where a result cache with an invalidation story earns its keep instead.
Choose consolidation when the hot read is identical across callers and tolerates sub-query-duration staleness. It flips when the reader needs its own write back, or when the query text carries per-caller parameters. A four-engineer team with one hot row should reach for a 200 ms application-level cache first: one fewer moving part, and the same effect.
Common weak answers
- "It is just a cache." A cache answers after the origin query finishes; consolidation shares an execution that is still running. The invalidation problem that dominates cache design does not exist here, and the connection-occupancy problem does.
- "Set the waiter cap high so nothing fails." The cap is the backpressure. Unset, it converts one slow query into thousands of stuck callers.
- "Turn it on everywhere for free throughput." On a primary serving read-your-writes traffic it silently returns stale data, and silent is the expensive part.