Free preview

The Spectrum of Failure Models

Why this matters: you cannot design fault tolerance without first saying precisely what a fault is. "The server might go down" is not a specification. Failure models are the specification — and each one demands a different mitigation.

Key takeaway

Failures in distributed systems are inevitable and may be transient or permanent. Failure models give you a framework for reasoning about a failure's impact and choosing the right mitigation. The models get strictly harder to handle as you move right along the spectrum.

The spectrum

Difficulty increases left to right, and the reason is consistent: detectability. A failure you can reliably detect is a failure you can route around. Everything on the right side is hard precisely because the healthy nodes cannot tell what is happening.

ModelWhat the node doesCan others detect it?Standard mitigation
Fail-stopHalts permanently, detectablyYes — reliablyRemove from the pool, fail over
CrashHalts silentlyNot directly — inferredHeartbeats plus timeouts
OmissionDrops messages it should send or receivePartially — looks like packet lossAcknowledgments, retries, sequence numbers
TemporalCorrect results, too late to be usefulOnly against a deadlineDeadlines, hedged requests, load shedding
ByzantineArbitrary — wrong data, lies, random behaviorOnly by cross-checking replicasChecksums, signatures, BFT quorums

Fail-stop

In a fail-stop failure, a node halts permanently, and other nodes can reliably detect the termination by communicating with it. Because the failure is detectable, fail-stop is the simplest model to handle: the system knows exactly who is gone and can redistribute the work.

This is the model most textbook algorithms assume — and the model real hardware rarely provides. It is closest to reality when a supervisor or orchestrator explicitly kills and reports a process.

Crash

In a crash failure, a node halts silently. Unlike fail-stop, other nodes cannot readily detect that it stopped working.

That single word — silently — is what makes distributed systems hard. From the outside, a crashed node is indistinguishable from a node that is merely slow, or reachable but partitioned away from you. You are forced to infer death from absence of evidence:

Omission failures

Omission failures occur when a node fails to send or receive messages. Two flavors:

  • Send omission: the node fails to respond to an incoming request.
  • Receive omission: the node fails to receive a request and therefore cannot acknowledge it.

These are nastier than crashes because the node is partly alive. It passes health checks, holds its place in the cluster, and drops a fraction of real work. A node that answers /healthz while silently dropping 30% of production requests will stay in your load balancer pool indefinitely.

Temporal failures

In a temporal failure, a node produces correct results but delivers them too late to be useful. Causes include inefficient algorithms, poor design choices, and loss of synchronization between processor clocks.

This is the dominant failure mode in modern systems, and the least respected. A node returning correct answers at 30 seconds when the deadline is 200 ms is, functionally, down — while looking perfectly healthy on every dashboard that tracks error rate.

The mitigation for the split-brain half is fencing: attach a monotonically increasing token to leadership, and have storage reject any write carrying a stale token. The returning zombie leader is then harmlessly ignored rather than trusted.

Byzantine failures

In a Byzantine failure, a node exhibits arbitrary behavior. It may transmit random messages, produce incorrect results, or stop unexpectedly. These failures typically stem from malicious attacks or software bugs, and they are the most difficult to mitigate — because the node is not silent, it is actively wrong, and confidently so.

Every other model assumes a failed node stops contributing. Byzantine assumes it contributes garbage that looks legitimate.

Gray failure: the model the spectrum misses

The hardest real-world failures are not any single point on the spectrum — they are the ones where the system's own view disagrees with its users' view.

ObserverWhat it seesConclusion
Health-check monitorProcess up, port open, 200 OKHealthy
Peer nodesHeartbeats arriving, a little lateHealthy
Actual usersp99 at 8 seconds, 4% of requests timing outBroken

This is gray failure, and it produces the outages that last hours — because every automated system reports green while the pager is silent and the customers are not. The defense is measuring what the user experiences (success rate and latency of real requests, from the client's side) rather than what the server believes about itself.

Choosing your model

You do not defend against all five. You declare which you handle and design accordingly:

Nearly every system you build inside one organization lands on crash + temporal: assume nodes stop or get slow, assume they do not lie, checksum anything that crosses a disk or a wire.

Key takeaway

The hard part of failure is never the failing node — it is that healthy nodes cannot tell what happened. Every mechanism in this lesson (heartbeats, deadlines, fencing tokens, quorums, checksums) exists to make decisions that stay safe while that ambiguity is unresolved.

Interview signal by level

LevelWhat a strong answer sounds like
L4"If a node goes down we fail over to a replica."
L5Distinguishes down from slow: "heartbeats detect crashes, but a slow node also needs a deadline — and I'd shed load rather than evict it."
Staff+Names the model and the safety mechanism: "crash plus temporal, non-Byzantine. Detection is inference, so failover needs fencing tokens or a paused leader returns and split-brains us. And I'd alert on client-observed success rate, because gray failure is invisible to health checks."

Next: all nine lessons applied to one design, end to end.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue