Part I — Foundations of Distributed Systems · Chapter 1

Distributed system models & failure modes

Hook

How long should a node wait for a reply before giving up — and once it gives up, what should it assume actually happened: did the peer stop, drop one message, or start lying?

Intuition
AB: delay 2✓ within Δ
AC: delay 9✗ exceeds Δ
AD: delay 3✓ within Δ
BA: delay 3✓ within Δ
CB: delay 5✓ within Δ
CD: delay 3✓ within Δ
At least one message exceeds Δ — under this bound, you can only assume the asynchronous model.

Six messages, all sent at slightly different times, all arriving with genuinely different delays. Drag Δ down and watch some of them start to "exceed the bound" — the same six messages, but now they no longer fit any single promise about how long you might have to wait.

Formalize

A system model is an assumption about message delay, not a fact about the network. The synchronous model assumes a known bound Δ\Delta on every message's delay; the asynchronous model assumes no such bound exists at all.

synchronous under Δ    m:delay(m)Δ\text{synchronous under } \Delta \iff \forall m: \text{delay}(m) \le \Delta
  • mm — a single message, with a known send time and delivery time.
  • delay(m)\text{delay}(m) — how long mm took to arrive: delivered time minus sent time.
  • Δ\Delta — the bound the synchronous model promises no message will exceed.

A node failure is a separate axis entirely — not about timing, but about what a node does when it's supposed to speak:

  1. Crash
    The node sends nothing further to anyone — total silence, forever.
  2. Omission
    The node sends the correct value to some peers, but silently drops it for others.
  3. Byzantine

    The node sends different peers conflicting values (or lies to everyone consistently) — behavior that can't be explained by silence or a dropped packet alone.

  4. Correct
    Anything reachable that isn't one of the above: every peer got exactly what was expected.
Play
AB: delay 2✓ within Δ
AC: delay 9✗ exceeds Δ
AD: delay 3✓ within Δ
BA: delay 3✓ within Δ
CB: delay 5✓ within Δ
CD: delay 3✓ within Δ
isSynchronousUnderBound(Δ=5) = false — the minimum working bound is Δ_min = 9.
A expected to send: B=1, C=1, D=1
A actually sent: B=1, C=1, D=1
classifyFailure(A) = correct

The top readout tracks Δmin=maxmdelay(m)\Delta_{\min} = \max_m \text{delay}(m) — the smallest bound that actually makes this round synchronous. Below it, step through each node's round log and watch classifyFailure name exactly what went wrong, straight from what it was expected to send versus what it actually sent.

Worked example

The six messages have delays 2, 9, 3, 3, 5, 3 (in MESSAGES order).

  1. Find the bottleneck

    The largest delay is 9 (A → C). No bound smaller than 9 can call this round synchronous — one message alone would violate it.

  2. Confirm the minimum bound
    Δmin=max(2,9,3,3,5,3)=9\Delta_{\min} = \max(2, 9, 3, 3, 5, 3) = 9

    At Δ=9\Delta = 9, every message qualifies. At Δ=8\Delta = 8, A → C alone breaks the promise.

  3. Now the four round logs
    • A sent every peer exactly what was expected → correct
    • B sent nothing at all → crash
    • C sent the right value to A and B but never reached D → omission
    • D told B a different value (5) than it told A and C (4) → byzantine, even though D never went silent
Checkpoint

For each node A–D, read its round log and classify what happened: correct, crash, omission, or byzantine. Get all four right.

A expected to send: B=1, C=1, D=1
A actually sent: B=1, C=1, D=1
Pick a classification for node A
Summary
synchronous under Δ    m:delay(m)Δ\text{synchronous under } \Delta \iff \forall m: \text{delay}(m) \le \Delta

Synchrony is a promise about time — every message arrives within a known bound, or it doesn't. Failure is a promise about honesty — a node stays silent (crash), goes selectively silent (omission), or actively lies (Byzantine). Every protocol in this course has to pick assumptions on both axes before it can promise anything at all.