This episode digs into how VTGate plans and routes queries to the right VTTablet, cross-shard query behavior with scatter-gather, SQL support and its limitations, and the failure modes you need to recognize.

Episode 4 organized data into shards. Now we dissect the engine that makes this distribution transparent to applications: VTGate. VTGate orchestrates every query — it decides which shard a query goes to, how results are merged when multiple shards are involved, and how queries are broken up so each shard's MySQL only receives the relevant work.
Episode 5 roadmap: VTGate's role in routing, the single-shard fast path, cross-shard queries and scatter-gather, query planning and SQL support, then the failure modes to watch out for. This is the episode you'll reach for most often when debugging queries in production.
VTGate is a stateless MySQL proxy facing the application. Its three core tasks: parsing (understanding the SQL), planning (building a cross-shard execution plan), and execution (sending to shards and merging results). Because it's stateless, VTGate is easy to scale — add VTGate Pods, add capacity.
Applications see one "giant MySQL". VTGate carries the complexity behind the scenes, including picking the right shard through a combination of VSchema and Topology Service.
A query that filters on the sharding key column is the fast path — VTGate computes the shard from the vindex value and sends the query to a single shard:
SELECT name FROM users WHERE user_id = 42Because user_id is the sharding key column with the hash vindex, VTGate knows exactly which shard stores user_id = 42. One round-trip, no merging. This is why schema design emphasizes a sharding key that queries use frequently.
A query without the sharding key column must touch all shards — this is called scatter or scatter-gather. For example:
SELECT COUNT(*) FROM users WHERE name = 'Arman'VTGate sends the query to every shard, receives partial results, then merges them. For COUNT(*) the results are summed; for ORDER BY and LIMIT VTGate performs a global sort and trim. This works, but the cost grows with the number of shards.
Important to understand: cross-shard JOINs are supported, with conditions. Joins between co-located tables (same sharding key) can be resolved in one shard. Joins that cross different shards force VTGate to gather and join in memory — it works but it's expensive.
vtctlclient VtGateExecute -json "SELECT COUNT(*) FROM users" commerceThe vtctlclient VtGateExecute command executes a query through VTGate from the admin side — useful for testing how VTGate handles a query without an application.
VTGate rewrites and splits queries before sending them to shards. The main technique is query splitting: a global query is broken into per-shard queries that only carry the relevant data. For example, a query with WHERE user_id IN (1, 2, 3) is split per shard based on where each value lives.
VTGate's SQL support is broad, but not total. Here's what you need to know:
SELECT, INSERT, UPDATE, DELETE, aggregations, co-located JOIN, single-shard transactions.JOIN (works but expensive), cross-shard GROUP BY (supported for certain aggregations), subqueries in certain forms.UPDATE without a sharding key filter) — Vitess will firmly reject these to prevent accidental updates beyond bounds.When a query is rejected, the error message is usually clear. This is a safety feature, not a bug: Vitess prefers refusing a dangerous query over executing it incorrectly.
Warning
UPDATE or DELETE without a filter on the sharding key column is usually rejected by Vitess. This is intentional: such a query doesn't know which shard to run on, and running it on all shards is a huge risk.
Some of the most common failures you'll hit at VTGate:
To check shard status and query health, vtctlclient is your best friend:
vtctlclient ListShardHealth
vtctlclient GetTablet -tablet <alias>One of the techniques VTGate uses most is query splitting. Take a query with repeated values:
SELECT * FROM users WHERE user_id IN (5, 1000, 2500000)If those three values live in different shards, VTGate splits the query into three per-shard queries, each loading only the relevant values, then merges the results. Each shard only works on its own portion — no shard carries the full query.
This pattern also applies to UPDATE and DELETE with IN — as long as the column is the sharding key. That's why a WHERE user_id IN (...) filter is actually recommended, not avoided. VTGate handles the splitting and merging automatically.
For queries that don't mention a keyspace, VTGate uses the keyspace selected via USE or the default. The keyspace-to-table mapping can be inspected directly:
vtctlclient GetVschema commercevtctlclient GetVschema displays the active VSchema — the map of tables, vindexes, and sharding status. When debugging query routing, this is the first place to verify that the configuration matches expectations.
In this episode 5 you understood how VTGate routes queries: the single-shard fast path for queries with the sharding key column, scatter-gather for cross-shard queries, query planning with splitting, plus the limits of SQL support and common failure modes.
Key takeaways:
UPDATE/DELETE without a sharding key filter for safety.In the next episode, episode 6, we keep availability high: replication and failover — primary and replica topology, automatic failover with vtctld and pseudo-GTID, and backup and recovery strategies. See you there!