Tail at scale, and why percentiles do not combine
Why does fan-out turn a healthy per-node p99 into a bad system p99, and why can't you average percentiles?
Tail at scale
(From Dean and Barroso's paper "The Tail at Scale".)
The arithmetic is short and the consequence large. Send a request to N independent nodes and wait for all. If each node is slow with probability p, then P(all fast) = (1 − p)^N.
Example: per-node p99 means p = 0.01. With N = 100: 1 − 0.99¹⁰⁰ = 1 − 0.366 = 63% of requests meet at least one slow node. Your p99 became your p63.
- probability that one node is slow (p = 0.01 for a per-node p99)
- number of independent nodes the request waits for
Data table
| Fan-out N | series | Requests slowed |
|---|---|---|
| 1 | per-node p99 | 1% |
| 1.12 | per-node p99 | 1.12% |
| 1.26 | per-node p99 | 1.26% |
| 1.41 | per-node p99 | 1.41% |
| 1.58 | per-node p99 | 1.58% |
| 1.78 | per-node p99 | 1.77% |
| 2 | per-node p99 | 1.99% |
| 2.24 | per-node p99 | 2.22% |
| 2.51 | per-node p99 | 2.49% |
| 2.82 | per-node p99 | 2.79% |
| 3.16 | per-node p99 | 3.13% |
| 3.55 | per-node p99 | 3.5% |
| 3.98 | per-node p99 | 3.92% |
| 4.47 | per-node p99 | 4.39% |
| 5.01 | per-node p99 | 4.91% |
| 5.62 | per-node p99 | 5.49% |
| 6.31 | per-node p99 | 6.14% |
| 7.08 | per-node p99 | 6.87% |
| 7.94 | per-node p99 | 7.67% |
| 8.91 | per-node p99 | 8.57% |
| 10 | per-node p99 | 9.56% |
| 11.2 | per-node p99 | 10.7% |
| 12.6 | per-node p99 | 11.9% |
| 14.1 | per-node p99 | 13.2% |
| 15.8 | per-node p99 | 14.7% |
| 17.8 | per-node p99 | 16.4% |
| 20 | per-node p99 | 18.2% |
| 22.4 | per-node p99 | 20.1% |
| 25.1 | per-node p99 | 22.3% |
| 28.2 | per-node p99 | 24.7% |
| 31.6 | per-node p99 | 27.2% |
| 35.5 | per-node p99 | 30% |
| 39.8 | per-node p99 | 33% |
| 44.7 | per-node p99 | 36.2% |
| 50.1 | per-node p99 | 39.6% |
| 56.2 | per-node p99 | 43.2% |
| 63.1 | per-node p99 | 47% |
| 70.8 | per-node p99 | 50.9% |
| 79.4 | per-node p99 | 55% |
| 89.1 | per-node p99 | 59.2% |
| 100 | per-node p99 | 63.4% |
| 112 | per-node p99 | 67.6% |
| 126 | per-node p99 | 71.8% |
| 141 | per-node p99 | 75.8% |
| 158 | per-node p99 | 79.7% |
| 178 | per-node p99 | 83.3% |
| 200 | per-node p99 | 86.5% |
| 224 | per-node p99 | 89.5% |
| 251 | per-node p99 | 92% |
| 282 | per-node p99 | 94.1% |
| 316 | per-node p99 | 95.8% |
| 355 | per-node p99 | 97.2% |
| 398 | per-node p99 | 98.2% |
| 447 | per-node p99 | 98.9% |
| 501 | per-node p99 | 99.4% |
| 562 | per-node p99 | 99.6% |
| 631 | per-node p99 | 99.8% |
| 708 | per-node p99 | 99.9% |
| 794 | per-node p99 | 100% |
| 891 | per-node p99 | 100% |
| 1K | per-node p99 | 100% |
| 1.12K | per-node p99 | 100% |
| 1.26K | per-node p99 | 100% |
| 1.41K | per-node p99 | 100% |
| 1.58K | per-node p99 | 100% |
| 1.78K | per-node p99 | 100% |
| 2K | per-node p99 | 100% |
| 2.24K | per-node p99 | 100% |
| 2.51K | per-node p99 | 100% |
| 2.82K | per-node p99 | 100% |
| 3.16K | per-node p99 | 100% |
| 3.55K | per-node p99 | 100% |
| 3.98K | per-node p99 | 100% |
| 4.47K | per-node p99 | 100% |
| 5.01K | per-node p99 | 100% |
| 5.62K | per-node p99 | 100% |
| 6.31K | per-node p99 | 100% |
| 7.08K | per-node p99 | 100% |
| 7.94K | per-node p99 | 100% |
| 8.91K | per-node p99 | 100% |
| 10K | per-node p99 | 100% |
| 1 | p99.9 | 0.1% |
| 1.12 | p99.9 | 0.112% |
| 1.26 | p99.9 | 0.126% |
| 1.41 | p99.9 | 0.141% |
| 1.58 | p99.9 | 0.158% |
| 1.78 | p99.9 | 0.178% |
| 2 | p99.9 | 0.199% |
| 2.24 | p99.9 | 0.224% |
| 2.51 | p99.9 | 0.251% |
| 2.82 | p99.9 | 0.282% |
| 3.16 | p99.9 | 0.316% |
| 3.55 | p99.9 | 0.354% |
| 3.98 | p99.9 | 0.398% |
| 4.47 | p99.9 | 0.446% |
| 5.01 | p99.9 | 0.5% |
| 5.62 | p99.9 | 0.561% |
| 6.31 | p99.9 | 0.629% |
| 7.08 | p99.9 | 0.706% |
| 7.94 | p99.9 | 0.792% |
| 8.91 | p99.9 | 0.888% |
| 10 | p99.9 | 0.996% |
| 11.2 | p99.9 | 1.12% |
| 12.6 | p99.9 | 1.25% |
| 14.1 | p99.9 | 1.4% |
| 15.8 | p99.9 | 1.57% |
| 17.8 | p99.9 | 1.76% |
| 20 | p99.9 | 1.98% |
| 22.4 | p99.9 | 2.21% |
| 25.1 | p99.9 | 2.48% |
| 28.2 | p99.9 | 2.78% |
| 31.6 | p99.9 | 3.11% |
| 35.5 | p99.9 | 3.49% |
| 39.8 | p99.9 | 3.9% |
| 44.7 | p99.9 | 4.37% |
| 50.1 | p99.9 | 4.89% |
| 56.2 | p99.9 | 5.47% |
| 63.1 | p99.9 | 6.12% |
| 70.8 | p99.9 | 6.84% |
| 79.4 | p99.9 | 7.64% |
| 89.1 | p99.9 | 8.53% |
| 100 | p99.9 | 9.52% |
| 112 | p99.9 | 10.6% |
| 126 | p99.9 | 11.8% |
| 141 | p99.9 | 13.2% |
| 158 | p99.9 | 14.7% |
| 178 | p99.9 | 16.3% |
| 200 | p99.9 | 18.1% |
| 224 | p99.9 | 20.1% |
| 251 | p99.9 | 22.2% |
| 282 | p99.9 | 24.6% |
| 316 | p99.9 | 27.1% |
| 355 | p99.9 | 29.9% |
| 398 | p99.9 | 32.9% |
| 447 | p99.9 | 36% |
| 501 | p99.9 | 39.4% |
| 562 | p99.9 | 43% |
| 631 | p99.9 | 46.8% |
| 708 | p99.9 | 50.8% |
| 794 | p99.9 | 54.8% |
| 891 | p99.9 | 59% |
| 1K | p99.9 | 63.2% |
| 1.12K | p99.9 | 67.5% |
| 1.26K | p99.9 | 71.6% |
| 1.41K | p99.9 | 75.7% |
| 1.58K | p99.9 | 79.5% |
| 1.78K | p99.9 | 83.1% |
| 2K | p99.9 | 86.4% |
| 2.24K | p99.9 | 89.4% |
| 2.51K | p99.9 | 91.9% |
| 2.82K | p99.9 | 94% |
| 3.16K | p99.9 | 95.8% |
| 3.55K | p99.9 | 97.1% |
| 3.98K | p99.9 | 98.1% |
| 4.47K | p99.9 | 98.9% |
| 5.01K | p99.9 | 99.3% |
| 5.62K | p99.9 | 99.6% |
| 6.31K | p99.9 | 99.8% |
| 7.08K | p99.9 | 99.9% |
| 7.94K | p99.9 | 100% |
| 8.91K | p99.9 | 100% |
| 10K | p99.9 | 100% |
| 1 | p99.99 | 0.01% |
| 1.12 | p99.99 | 0.011% |
| 1.26 | p99.99 | 0.013% |
| 1.41 | p99.99 | 0.014% |
| 1.58 | p99.99 | 0.016% |
| 1.78 | p99.99 | 0.018% |
| 2 | p99.99 | 0.02% |
| 2.24 | p99.99 | 0.022% |
| 2.51 | p99.99 | 0.025% |
| 2.82 | p99.99 | 0.028% |
| 3.16 | p99.99 | 0.032% |
| 3.55 | p99.99 | 0.036% |
| 3.98 | p99.99 | 0.04% |
| 4.47 | p99.99 | 0.045% |
| 5.01 | p99.99 | 0.05% |
| 5.62 | p99.99 | 0.056% |
| 6.31 | p99.99 | 0.063% |
| 7.08 | p99.99 | 0.071% |
| 7.94 | p99.99 | 0.079% |
| 8.91 | p99.99 | 0.089% |
| 10 | p99.99 | 0.1% |
| 11.2 | p99.99 | 0.112% |
| 12.6 | p99.99 | 0.126% |
| 14.1 | p99.99 | 0.141% |
| 15.8 | p99.99 | 0.158% |
| 17.8 | p99.99 | 0.178% |
| 20 | p99.99 | 0.199% |
| 22.4 | p99.99 | 0.224% |
| 25.1 | p99.99 | 0.251% |
| 28.2 | p99.99 | 0.281% |
| 31.6 | p99.99 | 0.316% |
| 35.5 | p99.99 | 0.354% |
| 39.8 | p99.99 | 0.397% |
| 44.7 | p99.99 | 0.446% |
| 50.1 | p99.99 | 0.5% |
| 56.2 | p99.99 | 0.561% |
| 63.1 | p99.99 | 0.629% |
| 70.8 | p99.99 | 0.705% |
| 79.4 | p99.99 | 0.791% |
| 89.1 | p99.99 | 0.887% |
| 100 | p99.99 | 0.995% |
| 112 | p99.99 | 1.12% |
| 126 | p99.99 | 1.25% |
| 141 | p99.99 | 1.4% |
| 158 | p99.99 | 1.57% |
| 178 | p99.99 | 1.76% |
| 200 | p99.99 | 1.98% |
| 224 | p99.99 | 2.21% |
| 251 | p99.99 | 2.48% |
| 282 | p99.99 | 2.78% |
| 316 | p99.99 | 3.11% |
| 355 | p99.99 | 3.49% |
| 398 | p99.99 | 3.9% |
| 447 | p99.99 | 4.37% |
| 501 | p99.99 | 4.89% |
| 562 | p99.99 | 5.47% |
| 631 | p99.99 | 6.11% |
| 708 | p99.99 | 6.84% |
| 794 | p99.99 | 7.64% |
| 891 | p99.99 | 8.53% |
| 1K | p99.99 | 9.52% |
| 1.12K | p99.99 | 10.6% |
| 1.26K | p99.99 | 11.8% |
| 1.41K | p99.99 | 13.2% |
| 1.58K | p99.99 | 14.7% |
| 1.78K | p99.99 | 16.3% |
| 2K | p99.99 | 18.1% |
| 2.24K | p99.99 | 20.1% |
| 2.51K | p99.99 | 22.2% |
| 2.82K | p99.99 | 24.6% |
| 3.16K | p99.99 | 27.1% |
| 3.55K | p99.99 | 29.9% |
| 3.98K | p99.99 | 32.8% |
| 4.47K | p99.99 | 36% |
| 5.01K | p99.99 | 39.4% |
| 5.62K | p99.99 | 43% |
| 6.31K | p99.99 | 46.8% |
| 7.08K | p99.99 | 50.7% |
| 7.94K | p99.99 | 54.8% |
| 8.91K | p99.99 | 59% |
| 10K | p99.99 | 63.2% |
Equivalently, the p99 of a 100-way fan-out is about the per-node p99.99. To get two nines from the system you need four nines from the parts. The more nodes you fan out to, the stricter the tail requirement, which makes the tail a distributed-systems problem, not a single-service problem.
Independence makes this estimate too optimistic. Correlated slowness (GC pauses, a shared hot resource, a bad rack, the SLC-cache cliff from Storage media — a consumer SSD that writes fast into a small fast buffer and slows suddenly when it fills) makes real behavior worse than the formula.
Mitigations all reduce variance, and trade resources for predictability:
- Hedged requests: send a copy to another replica after the p95 time has passed. A few percent more load, a tail improvement of about 10×.
- Tied requests: send to two replicas at once, each able to cancel the other.
- Micro-partitioning: make work units small, so a slow one costs little.
- Selective replication of hot data.
- Partial results with a deadline: answer with what you have at time T.
This is the branchless-code trade from CPU and memory: pay a small guaranteed cost to remove a large variable one.
Design conclusion: reduce your fan-out where you can. N is in the exponent. Reducing fan-out from 100 to 20 helps your tail more than making every node 2× faster.
Percentiles do not combine
The core mistake: p99 is not a quantity. It is a position in a distribution. Averaging positions has no meaning. Averaging ten servers' p99 values tells you nothing about the p99 of the fleet; you need the merged distribution.
Three common failures in real dashboards:
- Across servers. Merge distributions (histograms, t-digest, HDR histogram — compact structures that store the shape of a latency distribution), do not average summaries. This is why Prometheus histograms can be aggregated and Prometheus summaries cannot.
- Across time. Averaging 1-minute p99 values to get an hourly p99 is wrong. Store the buckets and compute the percentile when you query.
- Across sequential stages. p99(A) + p99(B) ≠ p99(A + B). If A and B are independent, the p99 of the sum is lower than the sum of p99s (both being slow together is rarer). If they are correlated, it can be higher. The simple sum is wrong in an unknown direction, which is worse than being wrong in a known one.
Most monitoring stacks average percentiles somewhere in the pipeline, silently. "We monitor p99" is a claim worth checking against your own dashboards (Check percentile aggregation in your monitoring).
What the performance laws forbid
What this article adds: the earlier parts explained why costs exist. This article explains why systems fail in a non-linear way. The queue hyperbola, the USL's N² term and the tail's exponent are all non-linear. Systems do not get worse in proportion. They are fine, fine, fine, and then fail suddenly. This is the most important operational fact across the platform.
| Law | What it forbids | Way out |
|---|---|---|
| Little's Law | Throughput without concurrency | Put more work in flight |
| Queueing (Kingman) | High utilization and low latency | Keep headroom, or reduce variance |
| Amdahl / USL | Unlimited speedup; and after N*, any speedup | Reduce serial fraction (α) and coordination (β) |
| Tail at scale | System tail ≤ component tail | Reduce fan-out, hedge, replicate |
And about averages: every law here is invisible in average-based monitoring. A system at 80% utilization looks fine on a dashboard and is one spike from collapse. A 100-way fan-out with healthy per-node p99 has a bad user-facing p99. Averaged p99 across a fleet can look flat while the real p99 doubles. The mathematics of this article is exactly the mathematics that averages destroy.