The idea: slow is rare, until you ask many servers

Most replies from a server come back fast, but a few are slow: the server was in a garbage-collection pause, its disk was busy, or a queue was briefly full. These few slow replies are the tail of the latency distribution, measured as the 99th percentile (p99) or the maximum. A user waits for the slowest reply the page needs, so the tail, not the median, is what users feel.

Bar chart: if each server is slow on 1 request in 100, a request that waits for 1 server is slow 1% of the time, for 10 servers 9.6%, for 100 servers 63%, for 1000 servers almost 100%
Fan-out turns a rare slow reply into the common case: 100 servers at 1% each make 63% of requests slow.

Large services fan out: one search or one page touches tens or hundreds of servers and waits for all of them. Then rare becomes normal. This is the problem in Dean and Barroso's paper The Tail at Scale, and its best-known fix is the hedged request.

The page runs one small scenario. A front end makes 10 reads, one after another, to three replicas R1–R3 that hold the same data. Each read normally takes 5–7 ms. Read #4 hits a GC pause on R1 (48 ms) and read #9 a busy disk on R3 (52 ms). Pick a policy and press Next Request or Run 10 Requests. The timeline shows each copy of the current read as a bar; the results table keeps one row per policy.

Hedged requests

A hedged request sends the read to one replica and starts a timer, the hedge delay. If the reply is back before the timer fires, nothing else happens. If not, the client sends a copy of the same read to a second replica. Whichever reply comes first is used, and the client cancels the other copy so that replica stops wasting work.

Time sequence: at t = 0 the client sends read #4 to R1, which is in a GC pause. At t = 10 ms there is still no reply, so the client sends a copy to R2. R2 answers 200 OK at t = 16 ms. The client sends cancel #4 to R1, which drops the work it would have finished at 48 ms
Read #4 with a 10 ms hedge: R2 answers at 16 ms, R1 is cancelled, and the read takes 16 ms instead of 48.

Without hedging the 10 reads take 5, 6, 5, 48, 6, 5, 7, 5, 52 and 6 ms: the median is 6 ms but the maximum is 52 (Demo 1). With a 10 ms hedge only #4 and #9 are still waiting when the timer fires. Each gets a copy to the next replica, which answers 6 ms later, so both finish at 16 ms. The maximum drops from 52 to 16 ms, and the cost is 12 copies instead of 10 (Demo 2).

Choosing the hedge delay

The delay decides how much extra load hedging adds. The usual choice is about the 95th percentile of normal latency: only the slowest 5% of requests get a second copy, so the load grows by about 5% while the tail shrinks a lot. In Google's BigTable test from the paper, hedging after 10 ms cut the 99.9th percentile of reading 1,000 keys from 1,800 ms to 74 ms, with only 2% more requests.

policyon the page: p50 / maxcopies for 10 reads
no hedging6 / 52 ms10
hedge after 25 ms (late)6 / 31 ms12
hedge after 10 ms (about p95)6 / 16 ms12
hedge after 3 ms (too early)6 / 9 ms20
2 copies at once5 / 6 ms20

Sending two copies at once gives the best latency but doubles the work on every replica (Demo 3). A hedge delay below normal latency does the same: at 3 ms every read is still running, so every read is copied (Demo 4). In a busy system doubled work means longer queues, which makes every request slower. The delay is set in Options; Demo 5 fills the table with the three policies side by side.

Cancellation and tied requests

  • Cancel the loser. Without the cancel the slow replica keeps working on a reply nobody reads.
  • Tied requests go one step further: both copies are sent at once, each tagged with the other server's address, and the first server to start the work tells the other to drop its copy. This removes queueing delay at almost no extra work.
  • Only for safe calls. Hedging runs the same call twice, so it fits reads and idempotent writes, not a payment.
  • Where it is used: gRPC has a hedgingPolicy (maxAttempts, hedgingDelay, nonFatalStatusCodes); Cassandra has speculative_retry (for example 99p); Envoy can hedge on a per-try timeout.

See also Retry, Exponential Backoff and Jitter (a retry waits for a failure, a hedge only for slowness), Circuit Breaker and Bulkhead, Latency vs Throughput, Java Garbage Collectors for where pauses come from, and Quorum Reads and Writes, another way to read from several replicas.

What the page leaves out

Network time; queueing, so the extra load of early hedges does not slow other requests on the page; requests running at the same time; real latency distributions and measuring the p95 to set the delay; tied requests (only in the text); hedge budgets that cap copies as a share of traffic; and fan-out itself (only in the first figure).

On the mind map

Open the Distributed Systems mind mapDistributedSystemsReplication: copies of data · what reads seeReplicationPartitioning: split data over nodesPartitioningTime and membership: order events, know who is aliveTime and membershipConsensus: agree on one value or logConsensusTransactions: one change across many servicesTransactionsMessaging: send events between systemsMessagingKafka internals: a replicated append-only logKafka internalsRedis internals: one thread, data in memoryRedis internalsKubernetes: reconcile desired stateKubernetesResilience and speed: stay up and fast under loadResilience and speedCache strategiesCache strategiescache-aside, write-through, write-behindCircuit breakerCircuit breakerstop calling a failing serviceToken vs leaky bucketToken vs leaky buckettoken allows bursts; leaky smoothsBackpressureBackpressurebounded queue: block, drop or ask lessLatency vs throughputLatency vs throughputnear full load, queues blow up p99Monolith vs microservicesMonolith vs microservicesfunction call vs network callDistributed tracingDistributed tracingtraceparent links spans across servicesRate limit windowsRate limit windowsfixed window lets 2x burst at the edgeRetry, backoff and jitterRetry, backoff and jitterWait 2^n plus random, or retries stampedeHedged requestsSlow? Send a copy; take the first replyOpen the Distributed Systems mind mapDistributed Systemsstay up and fast under loadResilience and speedCache strategiesCache strategiescache-aside, write-through, write-behindCircuit breakerCircuit breakerstop calling a failing serviceToken vs leaky bucketToken vs leaky buckettoken allows bursts; leaky smoothsBackpressureBackpressurebounded queue: block, drop or ask lessLatency vs throughputLatency vs throughputnear full load, queues blow up p99Monolith vs microservicesMonolith vs microservicesfunction call vs network callDistributed tracingDistributed tracingtraceparent links spans across servicesRate limit windowsRate limit windowsfixed window lets 2x burst at the edgeRetry, backoff and jitterRetry, backoff and jitterWait 2^n plus random, or retries stampedeHedged requestsSlow? Send a copy; take the first reply
This page on the Distributed Systems mind map · 79 pages in 10 branches · open it to see the whole map and test yourself in recall mode.