Distributed Compute

One Slow Task Sets the Pace for Everything

Behind a barrier, a job finishes when its slowest task finishes. Nine hundred and ninety-nine tasks completing in four seconds buys you nothing if the thousandth takes an hour. The counter-intuitive fix is to do the work twice on purpose — and it is only safe under conditions worth stating carefully.

▶ Run the lab

The question this answers

The question

Why is my job as slow as its worst task, and what can I do about it?

The guarantee — the property claimed, and its scope

With a barrier, the stage completes no earlier than max(task duration) — this is arithmetic, not a tuning failure. Speculative execution bounds that maximum in expectation, and only for tasks that are deterministic and side-effect-free, because it works by running the same task twice and discarding one result.

Everything below is bought to hold this sentence. "Strongly consistent" with no scope attached is a slogan, not a guarantee — read what it actually covers, and what it explicitly does not.

What a node knows — observation versus inference

The scheduler knows how long each task has been running and what the other tasks in the stage did. It does not know whether a slow task is nearly finished or barely started, whether it is slow because of its input or its machine, or whether launching a duplicate will help or simply consume a slot that another task needed. Speculation is a bet placed on partial information, which is why it is bounded rather than unlimited.

A node knows its own state and the messages that arrived. Everything else is inference from evidence that was already stale. "B has not replied in five seconds" is knowledge; "B is down" is a decision — and usually the bug.

What guarantee?What does a node know?How does it work?What can fail?How does it fail?Where is coordination?What holds under failure?How does it recover?How would you know?What is the simpler thing?
stragglersspeculative executionbarrierstail latencyskew

The barrier turns a distribution into its maximum

When a stage must complete before the next begins, the stage’s duration is the maximum over its tasks, not the average. That single substitution is why distributed jobs behave so differently from what an average-based intuition predicts. With a thousand tasks, you are sampling the tail of the duration distribution a thousand times, and whatever rare slowness exists in your infrastructure will be represented.

The arithmetic is unforgiving. If a task has a 1% chance of taking ten times as long — a disk retry, a garbage-collection storm, a noisy neighbour, a slow network path — then with 100 tasks you are more likely than not to have at least one, and with 1,000 you are essentially guaranteed several. Adding parallelism does not reduce this; it increases the number of chances for the rare event to occur. This is Fan Out to 100 and the Component’s Tail Becomes the System’s Median with a job as the request, and it is the reason tail latency is a first-class concern in compute and not just in serving.

The consequence worth internalising: improving the median task does almost nothing for a barrier-synchronised job. A change that makes every task 20% faster leaves the straggler roughly 20% faster too, which barely moves the maximum when the maximum is fifty times the median. The only changes that matter are ones that attack the maximum directly.

Tasks in the stageChance at least one task is 10× slow (p=1%)Expected stage time (median 4s)
10assumption~10%usually ~5s, sometimes 40s
100assumption~63%usually ~40s
1,000assumption~99.996%reliably ~40s — the tail is now certain
1,000 with speculationtypicalSame chance of a slow task~8s — the duplicate usually wins
The same task distribution, different task counts

Three causes that look identical and need different fixes

A slow task has three common causes, and the metric that separates them is input bytes per task.

Skew — the task has more data. Input bytes are far above the median, duration is proportional. Speculative execution does not help at all: the duplicate has the same input and takes the same time, and you have wasted a slot. The fix is to change the key or the partitioning, as in The Shuffle Is the Job and Hot Partitions: The Skew Hashing Cannot Fix.

A slow machine — the task has normal input and is running on a degraded host: a failing disk, a throttled CPU, a saturated network link, a noisy neighbour, an over-committed node. Input bytes are ordinary, duration is not. This is where speculative execution is exactly right, because the duplicate runs somewhere healthy.

Transient interference — a garbage-collection pause, a page-cache miss storm, a brief network problem. Indistinguishable from a slow machine at the moment of observation, and equally well handled by launching a duplicate.

So the diagnosis is a two-line check: if max input bytes is close to median input bytes, it is a machine problem and speculation helps. If max input bytes is far above median, it is skew and speculation is pure waste. Frameworks that launch speculative copies for skewed tasks are burning capacity, which is why speculation should be capped as a fraction of the stage.

stage 2 — reduce                      input bytes        duration
  median task                          3.1 GB             41s
  slowest task                       189.0 GB           3,140s
  ratio                                 61x                77x     → SKEW: change the key

stage 4 — reduce                      input bytes        duration
  median task                          2.8 GB             38s
  slowest task                         2.9 GB          1,890s
  ratio                                1.04x                50x     → SLOW HOST: speculate,
                                                                       then check that host
Two stages, two diagnoses, from the same two columns

Speculative execution: doing the work twice on purpose

The idea is deliberately wasteful and works well. When a task has been running substantially longer than its peers in the same stage, launch a second copy on a different machine and take whichever finishes first, killing the other. If the original was slow because of its host, the duplicate lands somewhere healthy and completes in normal time. The stage’s maximum collapses toward the median at the cost of a small amount of duplicated work — typically a few percent of the cluster, because only a few tasks are ever candidates.

The trade is capacity for tail latency, and the reason it is a good trade is that the last few tasks of a stage run in an otherwise idle cluster: the duplicate consumes a slot nobody else wanted. That is why speculation is normally enabled *late* in a stage and capped as a fraction of tasks, rather than applied to any task that happens to be slower than average.

The safety conditions are not optional. The task must be deterministic, or the winning attempt might produce a different answer from the losing one. It must be side-effect-free, or both attempts affect the outside world and the discard is meaningless — this is the same requirement MapReduce: The Model That Made the Trade-offs Visible imposes and the same trap teams fall into. And the output commit must be exclusive and atomic, so exactly one attempt’s result lands. Where those hold, speculation is free tail-latency insurance. Where they do not, it is a duplicate-execution generator that runs by default, and the first anyone hears of it is duplicate rows in a downstream table.

Note the relationship to Send a Second Request After p95 and Take Whichever Answers First: it is the same idea at a different scale. Send a second copy of the work, take the first answer, accept some duplicated effort for a much better tail. Both work for the same reason — the slow case is caused by something local to one machine, not by the work itself — and both fail for the same reason when applied to work whose slowness is intrinsic.

  • Launch a duplicate for tasks running far longer than their stage peers, late in the stage, capped as a fraction.
  • Take the first to finish; kill the other; commit exactly one result.
  • Safe only for deterministic, side-effect-free tasks with an exclusive commit.
  • Useless for skew — the duplicate has the same input and takes the same time.
  • The same idea as hedged requests, applied to tasks instead of RPCs.

The structural fixes: fewer barriers, smaller tasks

Speculation treats the symptom. Two structural changes attack the cause.

Remove or weaken the barrier. A barrier exists because a downstream task needs *all* upstream output. Where that is not truly required, pipelining lets downstream work begin on partial input and a slow producer delays only its own consumers rather than the whole stage. Streaming engines are built around this, and it is the largest available win — a job with no global barrier has no global straggler, only local ones.

Make tasks smaller and more numerous. With tasks much smaller than a worker’s capacity, a slow worker simply completes fewer of them and the rest of the cluster picks up the slack automatically. This is dynamic load balancing by task granularity, and it is the same instinct as work stealing inside a machine. The counter-pressure is real: more tasks means more scheduling overhead and, for a shuffle stage, more transfers — so the right size is a compromise, and "one task per core" is usually far too coarse.

There is also a cause worth naming and removing rather than tolerating: persistently degraded hosts. If speculation is regularly rescuing tasks from the same three machines, speculation is masking a hardware problem. Attribute speculative wins by host, and a pattern appears immediately. Fixing the machine is worth more than any amount of speculative work, and — like the retry attribution in Who Runs What, and What Happens When a Worker Goes Quiet — it is the automated mitigation that keeps the underlying fault invisible.

Barrier versus pipeline: where a slow task’s cost lands
whole job paysno global barrierlocal cost onlyStage 1: 999 tasks fast, 1 slowBARRIER wait for allPipelined downstream starts on partial inputStage 2 starts 1 hour late — everyone waitedOnly this consumer waits
ClientGateway / LBServiceWorkerDatabaseCacheQueue / LogObject storageCDNExternal system

Key points

  • Behind a barrier, stage duration is the maximum over tasks, so improving the median achieves nearly nothing.
  • More tasks means more chances to sample the tail; parallelism increases straggler probability rather than reducing it.
  • Input bytes per task, max versus median, separates skew from a slow host — and they need opposite responses.
  • Speculative execution collapses the maximum toward the median by running a duplicate and taking the first result.
  • It is safe only for deterministic, side-effect-free tasks with an exclusive commit, and useless against skew.
  • The structural fixes are fewer barriers and smaller tasks; the operational one is finding the machines speculation keeps rescuing.

The chain, answered

Every field here is required, which is why no lesson in this domain can recommend a design without naming what an operator sees when it fails, what survives the partition, what repairs it afterwards, and the simpler thing to consider first.

How it works
  • The scheduler tracks per-task progress and duration within a stage.
  • Late in the stage, it identifies tasks running substantially longer than their peers.
  • For a candidate task, it launches a duplicate attempt on a different worker, subject to a cap on total speculative work.
  • Both attempts run to completion independently, unaware of each other.
  • The first to finish commits its output through the exclusive commit path; the scheduler kills the other.
  • The losing attempt’s partial output is discarded, and the stage proceeds.
What can fail at the boundary
  • The straggler is caused by skew, so the duplicate is equally slow and a slot is wasted.
  • The task has external side effects and both attempts perform them.
  • The task is non-deterministic and the two attempts would have produced different results.
  • Speculation is launched too aggressively and consumes capacity other tasks needed.
  • Speculation masks a persistently degraded host that then affects everything else on it.
  • The stage is so large that even the speculative duplicate cannot finish inside the job’s deadline.
How it fails — what an operator sees
  • The job at 99%: 999 tasks done in minutes, one running for hours, and the cluster is otherwise idle. Wall time is that one task.
  • Speculation wasting a cluster: speculative task count is a large fraction of total tasks and none of them win, because the stragglers are skewed and every duplicate reruns the same oversized input.
  • Duplicated downstream effects: a table gains duplicate rows on exactly the nights when the job had a straggler — speculation ran a task with a side effect twice, and the job reported success both times.
  • The masked bad host: everything works, and speculative wins are concentrated on three machines out of four hundred. The mitigation is hiding a hardware fault that is also slowing everything else scheduled there.
  • Barrier stall in a stream job: throughput drops across an entire pipeline because one operator instance is slow and every downstream operator is waiting at an alignment point.
Where coordination is required
  • The barrier is the coordination that creates the problem: waiting for all upstream output converts a distribution into its maximum.
  • Speculation itself needs almost no coordination — the scheduler decides unilaterally and the commit resolves the race.
  • The exclusive commit is the one genuine agreement point, and everything about the safety of speculation rests on it.
  • Removing barriers is the structural way to reduce coordination, which is the general recommendation of Coordination Avoidance: Restructuring the Problem Instead of Paying for It applied to compute.
What still holds under failure
  • Correctness is preserved for deterministic, side-effect-free tasks regardless of how many duplicates run.
  • Capacity is consumed by duplicated work whether or not the duplicate wins.
  • If the original attempt fails outright, the speculative copy is already running, so speculation doubles as failure recovery.
  • A job with a straggler still completes; the cost is entirely wall time, which is what makes the problem easy to tolerate for years.
How it recovers
  • Detect: task duration max versus median per stage, alongside input bytes max versus median. Two ratios, complete diagnosis.
  • Contain: cap speculative work as a fraction of the stage so the mitigation cannot consume the cluster.
  • Recover: let the duplicate win, or for skew, repartition — salt the hot key, filter the placeholder, isolate the top keys.
  • Reconcile: attribute speculative wins by host and take repeat offenders out of service. Automated mitigation hides faults; attribution reveals them.
  • Verify: after a fix, compare the max-to-median duration ratio, not the average task time. The average was never the problem.
How you would know
  • Task duration distribution per stage, with max and median as first-class numbers rather than an average.
  • Input bytes per task, max versus median — the metric that decides whether speculation is worth launching at all.
  • Speculative task count and win rate; a high count with a low win rate means speculation is fighting skew.
  • Speculative wins grouped by host, which is how a persistently degraded machine becomes visible.
  • Time spent waiting at barriers per stage, which quantifies what removing a barrier would buy.
  • Job wall time versus the sum of task time divided by parallelism — the gap between them is the straggler tax.
When it helps
  • Large stages of homogeneous tasks on a shared cluster, where a small number of hosts will always be having a bad day.
  • Pre-emptible or spot capacity, where slow and disappearing workers are the normal condition.
  • Any job where wall-clock time matters and a few percent of extra compute does not.
When it hurts
  • Skewed workloads, where the duplicate is guaranteed to be equally slow.
  • Tasks with side effects, where speculation quietly becomes a duplicate-execution mechanism.
  • Clusters already at capacity, where a speculative task displaces a task that would have made real progress.
  • Very long tasks, where a duplicate started late has no chance of finishing first and simply burns a slot.
Simpler alternatives
  • Fix the skew instead: repartition, salt the hot key, filter placeholders, or isolate the top keys as a separate job.
  • Use smaller tasks so a slow worker completes fewer of them and the cluster rebalances naturally.
  • Remove the barrier by pipelining, so a slow producer delays only its consumers rather than the whole stage.
  • Detect and remove degraded hosts, which is the fix speculation is substituting for.
  • Accept the tail: for a nightly job with hours of slack, a straggler is a graph and not a problem.

One slow task, one barrier, and a job that runs at its speed

One slow task sets the pace for everything
Behind a barrier, a job finishes when its slowest task finishes. Nine hundred and ninety-nine tasks completing in four seconds buys you nothing if the thousandth takes an hour.
stage time (the max)
40.0 s
median task
4.0 s
chance ≥1 task is slow
100%
duplicate tasks launched
0
task 0
4.0 s
task 1
4.0 s
task 2
4.0 s
task 3
4.0 s
task 4
4.0 s
task 5
4.0 s
task 6
4.0 s
task 7
4.0 s
task 8
4.0 s
task 9
4.0 s
task 10
4.0 s
task 11
4.0 s
task 12
4.0 s
task 13
4.0 s
task 14
4.0 s
task 16 (slow)
40.0 s
↑ barrier releases at 40.0 s
showing 16 of 1,000 tasks · the job clock is the maximum, not the average
993 tasks finished in 4.0 s and bought you nothing. The stage takes 40.0 s because one task did. With a 1% chance per task, 1,000 tasks give a 100% chance of at least one — parallelism increases straggler probability rather than reducing it, because more tasks means more chances to sample the tail. Safety gate: speculation is safe only for tasks that are deterministic, side-effect-free, and committing through an exclusive atomic path. Outside those three conditions it is a duplicate-execution mechanism that is enabled by default.
Stage 1: 993 tasks fast, 7 slow
   │
   ▼
BARRIER — wait for all
   │
   ├─▶ Stage 2 starts 36.0 s late — everyone waited      (whole job pays)
   │
   └─▶ pipelined: downstream starts on partial input     (no global barrier)
          └─▶ only this consumer waits                   (local cost only)
Chance at least one task is 10× slow (p=1%)Expected stage time (median 4s)
10 tasksassumption~10%usually ~5s, sometimes 40s
100 tasksassumption~63%usually ~40s
1,000 tasksassumption~99.996%reliably ~40s — the tail is now certain
1,000 with speculationtypicalSame chance of a slow task~8s — the duplicate usually wins
The same task distribution, different task counts. More tasks means more chances to sample the tail.
assumptionThe probability figures assume task durations are independent. They are not: a correlated cause — a network event, shared storage, a cluster-wide GC pattern — makes many tasks slow at once, and speculation cannot help because there is nowhere healthy to run. Which task is slow is chosen deterministically from its own id, so the picture is reproducible.

Run it twice on purpose — when that is a fix and when it is waste

Two ratios are a complete diagnosis
Duration max ÷ median tells you there is a straggler. Input bytes max ÷ median tells you whether it is skew or a slow host — and they need opposite responses.
diagnose a stage
input bytes max ÷ median
1.04×
duration max ÷ median
50×
diagnosis
SLOW HOST
does speculation help?
yes
Input bytes are even (1.04×) and duration is not (50×). The task is slow for a reason that has nothing to do with its input: a degraded machine, a noisy neighbour, a GC pause, a page-cache miss storm, a brief network problem. The first is persistent and the rest are transient, and at observation time they are indistinguishable — which is fine, because a duplicate handles all of them equally well. Speculate, then go and look at that host.
stage p99, no speculation
58.98 s
stage p99, speculating
28.26 s
saved
30.72 s
extra cluster work
5.6%
threshold (× median): 1stage p99 with speculation12
Fanning out to 200 backends turns a 20000 ms per-call p99 into 58977 ms for the user: the slowest of 200 is what they wait for, so a rare slow call becomes a common slow request. Hedging after 12000 ms cuts that to 28260 ms (30717 ms saved) and costs 6% extra backend requests. That extra load is the whole trade — and it lands on a backend that is slow, which is the case where independence between the two copies is least true. Speculating at the median doubles cluster work for the stage; speculating at several times the median costs a few percent and captures most of the benefit, because only a few tasks are ever candidates. That trade is the whole decision, which is why both numbers are shown together.
Safe to enable. All three conditions hold: the duplicate computes the same answer, changes nothing outside the job, and only one of the two outputs is ever committed. The cost is a few percent of a stage and the upside is up to an order of magnitude off wall time.
Read the wins, not just the count. A high speculative launch count with a low win rate means speculation is fighting skew and losing. Wins concentrated on three machines out of four hundred means speculation is quietly rescuing you from a bad host every night — the wall time looks fine, and the machine is still broken.
assumptionThe tail model is log-normal fitted through the supplied p50 and p99, and it assumes the duplicate is independent of the original. That is exactly false when the task is slow because the machine is overloaded. Speculative work is usually a few percent of a stage and launched only late in it; both the threshold and the cap are framework defaults worth checking rather than trusting.

What people believe, and what is true

Claim

The job is slow, so the tasks are slow.

Reality

Behind a barrier the job is as slow as one task. The other 999 may be finishing in seconds.

Claim

More parallelism reduces the straggler problem.

Reality

It increases it. Every additional task is another sample from the tail of the duration distribution.

Claim

Speculative execution is wasteful.

Reality

It costs a few percent of capacity, usually in an otherwise idle window at the end of a stage, and can cut wall time by an order of magnitude. It is one of the best trades available.

Claim

Speculation fixes slow tasks.

Reality

It fixes tasks that are slow because of their host. A task that is slow because of its input will be exactly as slow on the second host.

Claim

Speculation is safe because the framework handles it.

Reality

The framework handles its own output commit. If your task writes to an external system, speculation is duplicating that write and nothing will tell you.

Go deeper

Only the levels this lesson can honestly fill — a missing level is a claim nobody had.

Overview

A job behind a barrier finishes when its slowest task finishes. Run a duplicate of the slow one somewhere else and take whichever finishes first — as long as running the task twice is harmless.

Practical

Read two ratios per stage: duration max over median, and input bytes max over median. Similar input with wildly different duration means a bad host, and speculation is the fix. Very different input means skew, and speculation is waste — change the key instead. Then attribute speculative wins by host and fix the machines that keep needing rescue.

Advanced

Speculation is a bet that slowness is a property of the machine rather than of the work, and it pays exactly when that bet is right. This is the same bet Send a Second Request After p95 and Take Whichever Answers First makes for RPCs and the same one a replicated read makes when it queries two replicas and takes the first answer. Seen that way, the general principle is: where you have spare capacity and the slow case is caused by something local, duplicating work is a legitimate and often optimal way to buy tail latency. Where slowness is intrinsic to the work — skew, a genuinely large input, a correlated cluster-wide event — duplication buys nothing and costs capacity, and the only real fix is to change the work.

Apply it

Interview questions
  • 💬 Your job is at 99% for an hour. What are the two metrics you look at, and what does each tell you?
  • 💬 Why does making every task 20% faster barely help a barrier-synchronised job?
  • 💬 When is speculative execution unsafe, and what specifically breaks?
  • 💬 Speculation is rescuing tasks on the same three hosts every night. What is the real problem?