CS PAPERS DEEP-READ · PAPER 27

The Tail at Scale

Dean & Barroso · Google · CACM 2013

中文 →

What did this paper do?

In 2013, Google's Jeff Dean and Luiz Barroso wrote a short article — a few pages — called "The Tail at Scale," about something nobody running a large website can dodge: why a system that is "fast on average" still hands users visible stalls. Their answer isn't "make every machine faster" — you can't — it's to admit machines will occasionally be slow, then let the software absorb that slowness.

First, something odd

Suppose your machines are good citizens: each answers within a second 99% of the time and only dozes off 1% of the time. But one modern web search must ask a hundred machines at once and wait for all of them before it can assemble a result. And then — more than six out of ten requests take over a second. Every machine scores "99 out of 100," yet together they fail six times in ten.

The reason is plain: you're not waiting on the average, you're waiting on the slowest one. One person can always be punctual; a team of twenty almost never arrives together — one latecomer and everyone waits. Scale doesn't add up the good luck, it amplifies the bad luck.

And that "dozing off" isn't a machine breaking. It's a machine with other things on its plate: someone else's program on the same box is grabbing CPU and memory; a background chore occasionally tidies up accumulated data; memory garbage has to be collected, and collecting means pausing the real work to sweep; a machine that runs hot throttles itself; and requests queue up layer after layer. Each of these only costs a few milliseconds now and then — but if you're waiting on hundreds of machines at once, one of them is always mid-nap.

The idea: treat "slow" as a fault to tolerate

Engineering went through an important change of mindset once: we stopped hoping machines wouldn't break, assumed they would, and kept spare copies so the system keeps working when one dies — that's fault tolerance. This article says: do the same thing for slowness.

The most direct move is a backup request: after sending a request to one machine, if it hasn't answered in a while, ask a second machine that holds the same data, use whichever answers first, and cancel the other. The beauty is how cheap it is — only that small minority of slow requests ever triggers a second ask, so you send well under a few percent extra traffic, while the slowest sliver of requests drops from "a second or two" to "tens of milliseconds." The other move is to dodge in advance: if a machine has been visibly sluggish lately, pull it off the roster and watch it for a while — drop one slow machine and the whole thing gets faster.

One delightfully counterintuitive trick

For unavoidable chores like background cleanup, intuition says "stagger them, don't let them collide." The article says the opposite: make all the machines do it at the same time. Once you see it, it's obvious — if they're staggered, at any given moment some machine is busy sweeping, and since you always wait for all of them, every single request gets dragged; if they all sweep together, only the requests in that one short window suffer and the rest of the time is spotless.

An honest note: most of these moves buy stability with slack — asking twice and keeping spare replicas both require idle capacity. On a cluster already running flat out, the extra requests will drag everyone down instead.

Remember one thing

Across hundreds or thousands of machines you wait on the slowest one, so "occasionally slow" gets amplified by scale into "often slow." Since you can't cure it, attack "machines get slow" the way we once attacked "machines break": ask a second machine, take whoever's first, bunch the chores into one moment, pull the slow machines out — teach the system to tolerate the tail.

Want the amplification curve, the hedged/tied request timelines, and the measured numbers? → switch to the deep read