Skip to content
nonconvexcard

Tail Latency and Fan-Out Amplification

Why a request that touches a hundred machines is slow even when every machine is fast, and what hedging buys — derived from the distribution of a maximum.

AI SystemsAdvanced⏱ 1 hour8 min readtail latencyfan-outorder statisticshedgingstraggler
2 problems →

The maximum is the parent

PropositionThe distribution of the slowest of n calls

Let a parent request issue nn sub-requests with latencies X1,…,XnX_1,\dots,X_n, independent and identically distributed with CDF FF, and let the parent complete when the last child does. Then the parent latency Mn=max⁡iXiM_n = \max_i X_i has

Pr⁡[Mn≤t]  =  F(t)n,Pr⁡[Mn>t]  =  1−(1−Fˉ(t))n  ≤  n Fˉ(t),\Pr[M_n \le t] \;=\; F(t)^n , \qquad \Pr[M_n > t] \;=\; 1 - \bigl(1-\bar F(t)\bigr)^n \;\le\; n\,\bar F(t) ,

where Fˉ=1−F\bar F = 1-F is the tail function.

CorollaryFan-out shifts which quantile you must control

To achieve Pr⁡[Mn≤t]≥p\Pr[M_n \le t] \ge p it is necessary and sufficient that each child satisfies F(t)≥p1/nF(t) \ge p^{1/n} — that is, tt must be the child's p1/np^{1/n} quantile. For p=0.99p = 0.99:

n1101001000p1/n0.990.998990.9998990.9999899\begin{array}{r|cccc} n & 1 & 10 & 100 & 1000\\ \hline p^{1/n} & 0.99 & 0.99899 & 0.999899 & 0.9999899 \end{array}

So a hundred-way fan-out turns a p99 target on the parent into a p99.99 target on each child.

Key idea

Fan-out does not make any individual service slower. It changes which quantile of that service the user experiences. A service optimised to a p99 target is, under 100-way fan-out, being consumed at its p99.99 — a part of the distribution nobody measured, nobody tuned, and which is usually dominated by effects invisible at p99: garbage collection, a cold cache, a retry, a kernel scheduling hiccup.

What hedging buys

The standard countermeasure is to send a second copy of a sub-request if the first has not returned by some deadline dd, and take whichever answers first.

PropositionA hedged request truncates the tail at the hedge delay

Let XX be the latency of one attempt, with tail Fˉ\bar F. Issue a second, independent attempt at time dd if the first is still outstanding, and let YY be the time until the first response. Then for t>dt > d,

Pr⁡[Y>t]  =  Fˉ(t)⋅Fˉ(t−d),\Pr[Y > t] \;=\; \bar F(t)\cdot \bar F(t-d) ,

and for t≤dt \le d, Pr⁡[Y>t]=Fˉ(t)\Pr[Y>t] = \bar F(t).

Assumptions

  • The two attempts are independent — different replica, different host, no shared bottleneck. A hedge routed to the same overloaded machine buys nothing, which is the common failure mode.
  • Issuing the hedge does not slow the original. At low hedge rates this is nearly true; at high rates it is false, and the cost proposition below is what bounds the damage.

Take dd to be the p95 of XX. Then for tt beyond dd the tail is the product of two small numbers rather than one. If Fˉ\bar F decays exponentially with scale τ\tau, so Fˉ(t)=e−t/τ\bar F(t) = e^{-t/\tau}, the hedged tail is e−(2t−d)/τe^{-(2t-d)/\tau} — the decay rate has doubled.

PropositionThe extra load a hedge creates

If the hedge fires at delay d=qp(X)d = q_p(X), the expected number of attempts per logical request is

E[#attempts]  =  1+Pr⁡[X>d]  =  1+(1−p),\E[\#\text{attempts}] \;=\; 1 + \Pr[X > d] \;=\; 1 + (1-p) ,

so hedging at the p95 costs at most 5% additional load, and at the p99 at most 1%.

Key idea

Hedging is unusually cheap because it is conditional: it only duplicates the requests that are already slow, and by construction those are rare. Five percent extra capacity to square the tail is one of the best trades available in a serving system — provided the hedge lands somewhere genuinely independent, and provided the system has the 5% to spare, which by the utilisation wall it will only have if it is not already near saturation.

Reading the numbers

Two consequences are worth carrying around.

  1. Measure the quantile your architecture actually consumes. If a request fans out nn ways, instrument the child at 1−(1−p)/n1 - (1-p)/n, not at pp. For n=100n = 100 and a p99 parent target that is p99.99 — which needs on the order of 10510^5 samples to estimate at all, a measurement problem in its own right.
  2. Reducing fan-out is a latency optimisation. Halving the number of shards a query touches moves the consumed quantile from p1/np^{1/n} to p2/np^{2/n}, which is a strictly easier target. This is a real argument for larger shards, and it trades against the memory and pooling arguments elsewhere in this field.

What this does not say

This page treats the children as independent draws from a fixed distribution. It does not model the case where the slow child is slow because of the parent's own load — a self-inflicted straggler. Nor does it cover the case where partial results are acceptable: if the parent can answer with 95 of 100 shards, the relevant order statistic is X(95)X_{(95)} rather than the maximum, and the fan-out penalty largely disappears. Designing for partial results is the strongest available defence against tail amplification, and it is an application-level decision rather than an infrastructure one.

References

  • PaperJ. Dean, L. A. Barroso (2013). The Tail at ScaleSource of the hedged-request and tied-request techniques discussed here.
  • PaperA. Vulimiri, P. B. Godfrey, R. Mittal, J. Sherry, S. Ratnasamy, S. Shenker (2013). Low Latency via RedundancyAnalyses when duplicating a request reduces latency and when it backfires through added load.

These works are cited as sources to read, not reproduced. All exposition, examples and problems on this site are written independently; where an argument is standard, the citation points to a canonical treatment.

Results stated on this page

Problems (2)

All problems