Definition · Scatter-gatherA scatter-gather query is one whose predicate does not determine the shard, so the router sends it to all N shards (scatter) and combines the partial results (gather). A routed query names the partition key and goes to one shard. The request amplification of a scatter-gather is N: shard requests per client query.
Which queries are unroutable is decided by the partitioning function and nothing else. Partition by user and “everything for user 91” is routed while “everything created yesterday” is a scatter; partition by day and the two swap. Every partitioning choice makes one family of queries cheap and the rest of them scatters, which is why the choice belongs with the access patterns rather than with the data model.
N = 24 shards, 400 client queries a second
shard requests = 24 × 400 = 9 600 /s
amplification = 24×
routed instead = 400 /s
per-shard latency, in ms with weights out of 100
10:80 25:15 90:4 250:1
one shard mean 17.85 ms p50 10 p90 25 p99 90 ms
the query N = 24 p99 250 ms
exact 250 ms, and 250 ms in a seeded run of 400 queries
The amplification is the figure that surprises people, because the sentence “we sharded, so each node does less work” is true and is not the whole of it. Each node does less work per query; the node receives N times as many queries, so a fleet serving 400 client queries a second is handling 9 600 requests a second. Everything that is priced per request — a connection, a lock acquisition, a log line, a tracing span — is multiplied by 24.
The tail, again
the same per-shard distribution, against the shard count
N shard requests /s query mean (ms) p50 p90 p99
1 400 17.85 10 25 90
2 800 24.92 10 25 250
4 1 600 37.22 25 90 250
8 3 200 56.72 25 90 250
16 6 400 84.74 90 250 250
24 9 600 105.24 90 250 250
32 12 800 121.40 90 250 250
64 25 600 163.47 90 250 250
At two shards the query's p99 is already 250 ms — the worst outcome in the per-shard distribution — because 0.99² = 0.9801, which is below the 0.99 a p99 needs. By sixteen shards the p90 has reached 250 ms too. The per-shard distribution never changed; the query is simply asking more shards to all be fast at once, and “Tail Amplification under Fan-out” is the same calculation stated in general.
Example · The third effect: consumer parallelismA partition is also the unit of parallel consumption. If a downstream reader wants to process this dataset with more than N workers, it cannot: worker N + 1 has no partition to read, and at N = 24 the ceiling is 24 regardless of how much hardware is available. So the partition count that was chosen for storage and load has also set a limit on a pipeline nobody in that conversation was thinking about, and raising it later means the rehash of “Rehashing When N Changes”.
What to do instead
The direct fix is to make the query routable, by partitioning on the attribute it filters on — which usually means another copy of the data partitioned differently, and that is exactly the global secondary index of the next lesson. Failing that, the levers are the ones from “Stragglers”: narrow the per-shard tail, accept a partial result, or hedge. What does not help is raising N, which makes both costs worse.
It is worth writing the two numbers down together whenever a scatter-gather is proposed, because they trade against each other in a way that a single figure hides. 9 600 requests a second is a capacity question and 250 ms is a latency question; a fleet can be sized for the first and still fail the second, and a tail fix that adds hedged requests makes the first worse.