How do independent tasks agree on when to proceed?

Coordination & Limits

Barriers and latches, `Promise.all` and `gather` and what they do on failure, the sequential-await trap, and the counterweight: parallelising a call fan-out multiplies load on whatever is downstream. Thundering herds and single-flight coalescing.

Barriers▶ lab

Every participant stops at the line until all of them arrive, then all of them continue. It is the primitive for phased computation — and its two characteristic failures are a participant that never arrives, which hangs everyone, and code that reads the next phase's data before the barrier, which produces a wrong answer with no error.

Q · How do N workers agree that a phase is finished before any of them starts the next one?

Latches & Countdowns▶ lab

One-shot and asymmetric: waiters wait, workers count down, and when the count reaches zero the gate opens permanently. 3 → 2 → 1 → 0 and it never resets — which is both the whole appeal and the thing people get wrong when they reach for it expecting a barrier.

Q · How does a coordinator wait for N pieces of work to finish, without knowing which one finishes last?

Promise.all & gather▶ lab

Start N tasks, wait for the collection, aggregate the results. The easy part is the fan-in; the lesson is the failure behaviour — Promise.all rejects the instant one task fails while the other N−1 keep running unsupervised, and every language offers a different, incompatible way to say "tell me about all of them".

Q · When one of five parallel calls fails, what happens to the other four — and what does my caller actually learn?

The Sequential Await Trap▶ lab

Three independent awaits in a row take the sum of their latencies for no reason at all. The fix is four characters of syntax and it is genuinely correct — but read the warning before you ship it, because the very next lesson is about the database this fix knocks over.

Q · These three calls do not depend on each other — why does the endpoint take the sum of their latencies?

Parallelism Moves the Load Downstream▶ lab

One request became one hundred parallel database queries. The endpoint got three times faster on the developer's laptop and the database fell over in production. This is the counterweight to the previous lesson: parallelising a fan-out does not reduce work, it concentrates it, and the system that absorbs the concentration is never the one you were optimising.

Q · The endpoint got faster and the database got slower — where did the load actually go?

Thundering Herd▶ lab

One event wakes ten thousand waiting tasks and every one of them does the same thing to the same resource in the same millisecond. The trigger is usually benign — a cache entry expiring, a connection restored, a scheduled job at :00 — and the response is always the same shape: spread it, batch it, bound it, or do it once for everyone.

Q · Why does one small event produce a load spike ten thousand times its size, and how do I stop the crowd forming?

Single-Flight Coalescing▶ lab

One hundred callers need the same value. Issue one fetch and give everyone the same result. The implementation is fifteen lines and two of them are the ones that go wrong: what the dedup key includes, and what happens to the shared in-flight entry when the fetch fails.

Q · A hundred concurrent callers want the same value — how do I make that one fetch, and what do they all get when it fails?