When part of a distributed system fails, what does it take to bring the whole system back to a correct state?
A dependable system keeps providing correct service despite threats. Threats are modelled as faults, which cause errors in the state, which become failures when they reach the interface. Dependability is measured by attributes such as availability and reliability. Its basic means of tolerance is rollback recovery: save the state (checkpointing), save the events since then (logging), restart from them. In a distributed system, local checkpoints are only usable together if they form a consistent global state, messages in transit included. Independent checkpoints may never form one; coordinated protocols (Tamir & Sequin, blocking; Chandy & Lamport, non-blocking) build one on purpose. Logging then replays what came after, provided execution is piecewise deterministic.
Distributed systems run services we rely on daily: finance (online banking, stock trading), e-commerce, civil infrastructure (power grid, traffic control), entertainment (gaming, streaming), personal data storage (Dropbox, Google Drive, SkyDrive). A data centre brought down by a failure can cost tens to hundreds of thousands of euros per hour, in wasted expenses and lost revenue.
Dependability is not cheap either, which is possibly why nearly 60% of Fortune 500 companies suffer more than an hour and a half of downtime per week. A data centre costs from $450 per square foot for 99.671% availability (28.8 hours of downtime a year) to $1,100 for 99.995% (0.4 hours). To reduce that cost, the slides propose training more “engineers against downtime”.
The Oxford dictionary defines dependability as “the quality of being able to be relied on to do what somebody wants or needs”, synonym reliability. In systems engineering, a field much wider than computing, the two are closely related but not the same thing (§3).
In distributed computing, dependability is the ability of a distributed system to provide correct services to its users despite the many different threats: undetected software defects, hardware failures, malicious attacks, …
Reasoning about it needs a model of the system and a model of the threats.
A system provides services to its users (clients), each through an interface; a functional specification defines what each should do. At each moment the system is in a state, determined collectively by its processes and threads: registers, stack, heap, file descriptors, kernel state… The part visible through user interaction is the external (observable) state, basically the abstract state of the specification. The rest is internal state.
The state of a system at time t is the (minimum amount of) information that, together with knowledge of the dynamics of a (deterministic) system, allows an observer to completely describe the system's future behaviour from t on.
So state serves recovery: a system can be brought back to where it was before a failure if its state was (consistently) captured and not lost, e.g. serialised to stable storage and intact after the failure. How do we capture the state of a distributed system at time t? First, fix the boundary, the line between the system's components (inside) and its environment: every other system that affects it in any way. Whether something is a system or a component depends on the level of abstraction. Second, “at time t” means little without a global clock (Ch. 2); §6 does without one.
A system not complying with its functional specification has a (service) failure, caused by wrong values in its state (errors), which in the causal model are caused by faults. So threats are modelled as sorts of faults. In Tanenbaum & van Steen's words:
A system fails when it does not behave as promised. An error is a part of a system's state that might have caused a failure. The cause of an error is a fault.
A fault may be dormant: a bug harmless until its code runs, an unlocked shared variable harmless until two threads update it concurrently. When its condition is met, the fault is activated into an error, which propagates through interactions; when it reaches the interface, a service failure occurs. Since systems are composed recursively, a component's failure is a fault of the larger system. This is the chain of threats, and why “fault” and “failure” are often, somewhat improperly, used interchangeably.
Different sorts need different treatment. Six criteria:
Under manifestation, Byzantine means telling different parties different things, which a bug can do as well as an attacker. A commission fault is doing something that should not have been done, as opposed to omitting something.
Tanenbaum & van Steen classify failures by a server's observable behaviour:
| Type of failure | Description of server's behaviour |
|---|---|
| Crash | halts, but is working correctly until it halts |
| Omission | fails to respond to incoming requests |
| receive omission | fails to receive incoming messages |
| send omission | fails to send messages |
| Timing | response lies outside a specified time interval |
| Response | response is incorrect |
| value failure | the value of the response is wrong |
| state-transition failure | deviates from the correct flow of control |
| Arbitrary (Byzantine) | may produce arbitrary responses at arbitrary times |
Two lists from two books, overlapping but different: keep them apart.
Availability and reliability can serve as evaluation metrics; the other three are difficult to quantify. Availability, reliability and integrity are fundamental to all distributed systems; maintainability and safety are secondary or not always applicable.
Availability factors
Five nines means 99.999%: a 10−5 probability of finding the system unavailable, at most 5.256 minutes of downtime a year. Reliability is the probability of correct service for a period Δt:
R(Δt) = e−λΔt, λ ∈ [0, ∞) the failure rateThe slide adds “≈ proportional to MTTF” under λ.
And availability ≠ reliability: a system failing frequently but recovering very quickly may have high availability and very low reliability.
Set MTTF and MTTR, or pick a preset. Of the first two presets, which is more available, and which would you trust to run for a week?
Zhao lists four approaches:
Applications needing high availability but not necessarily high reliability could be served by logging and checkpointing; more demanding ones by recovery-oriented computing. Both rely on rollback recovery: going back to the most recent recorded correct state. Alternatively, failures can be masked, hidden from other processes, by redundancy:
M1's lessons: dependability is essential, it needs models of systems and threats, and the failure-handling techniques determine the level reached. The rest of this chapter follows the rollback route.
C2 starts from interaction. Components (with their functions, services, tasks, goals…) do not work in isolation: they interact over time and space because they depend on each other, and a “mischievously simple” way to model dependencies is causality. In general causality is subtle: a human cognitive tool (“mind reading” others' goals), a word nineteenth-century physics papers used in titles but almost never in the text, and not the same as correlation (the tobacco industry's hypothetical genotype causing both cancer and smoking). Pearl [2009] makes it precise. Distributed systems need less: cause tied to the system model, defining dependencies between processes, with causes preceding effects. That is happens-before (Ch. 2).
A checkpoint of a distributed system is a copy of the system state. If it is available after the system fails, it can be used to recover the system to the state it had when the checkpoint was taken. Checkpointing is (periodically) taking a copy of the system state and saving it to a stable storage that can survive the faults tolerated.
To recover to the point right before the failure, all incoming messages are typically logged too, and possibly other nondeterministic events. Both are rollback recovery: back to a state prior to the failure. Roll-forward mechanisms exist but typically cost more runtime overhead and physical resources.
Checkpointing and logging are the most fundamental dependability techniques, used at every level. Alone, they are relatively easy to implement with low runtime overhead, but limited: with checkpointing only, some information can be lost, and recovery is typically slower than with more sophisticated approaches.
N processes interact by message exchange, and with the outside world through input and output.
Fault model
A process's state is, from the operating system's viewpoint, its entire address space, which a generic checkpointing library saves whole. Application semantics can define a smaller one. Stable storage is essential: it survives process failures, so its contents are available to the recovering process, and all checkpoints and logged messages must go there. Its forms:
The state of a distributed system, its global state, includes the state of every process. Aggregating them is not enough: messages move information between processes and change their states, so processes causally depend on each other, and dependencies cannot be lost in the global state.
The slides' example: processes P0, P1, P2 with checkpoints C0, C1, C2 and messages m0 (P0 → P1), m1 (P1 → P2). P0 and P1 are bank accounts A and B; m0 is a $100 deposit from A to B.
C1 reflects the reception of m0, C0 does not reflect its sending. If P0 crashes after m0 and P1 after C1, recovering from C0 and C1 makes $100 appear from nowhere.
C0 reflects the sending of m0, C1 its reception; likewise C1, C2 and m1. Recovering from C0 and C1 moves the $100 correctly from A to B.
m0 and m1 are sent before the checkpoints and received after them. The state is reachable, but recovery loses the dependencies on m0 and m1: the $100 may disappear. The problem is the messages in transit.
A global state from a set of checkpoints is a consistent global state if it is reachable from the initial state of the system. Otherwise it is inconsistent: its checkpoints are incompatible, not admissible, and cannot be used for recovery.
A set of N processes, each with a set of states (one of them initial) and a set of events that trigger its changes of state. A set of channels, each a uni-directional reliable communication channel between two processes (a TCP connection is two channels). The channel state is the set of messages in transit along the channel, not yet received by the target process.
In scenario (c), if m0 is saved in C0 as channel state and m1 in C1, recovery becomes possible. A usable global state is consistent process states plus channel states.
A set of local checkpoints draws a cut across the space-time diagram. It is consistent iff every message received inside it was also sent inside it, i.e. it contains the causal past of each of its events (Ch. 2). A message received inside but sent outside is an orphan message, an effect without its cause: scenario (a). One sent inside and received outside is exactly channel state: scenario (c).
The dashed line is a candidate global state. Load the slides' scenarios, then click events to move each process's frontier. Try to make m2 an orphan.
Checkpoint-based protocols focus on state rather than events, so they do not rely on the piecewise deterministic assumption (§9). They are simpler and less restrictive: no need to identify and log every nondeterministic event. The price: unless a checkpoint precedes every event, which is typically not realistic, some execution is lost.
In uncoordinated checkpointing, each process autonomously decides when to checkpoint. After a failure, recovery goes backwards to select the most recent set of consistent checkpoints. The main problem: the checkpoints might not allow a consistent global state to be reconstructed. To select one, the dependencies between checkpoints must be determined and recorded with each checkpoint, which adds overhead and complexity.
Rolling a process back undoes its sends, so their receipts become orphans; each receiver rolls back before its receipt, undoing its sends, and so on. With an unlucky pattern, this domino effect pushes every process back to its initial state.
Green bars are checkpoints. Crash P0 on the uncoordinated run and read the rollback trace; then crash either process on the coordinated run (same messages).
Tamir & Sequin [1984] is a coordinated checkpointing protocol. A coordinator knows all the other processes, the participants, and uses two-phase commit to make the local checkpoints mutually consistent. The round is an atomic transaction: either all processes create a new set of checkpoints, or they abandon it and revert to the previous set. Phase one creates a quiescent point of the system state; phase two atomically moves from the old checkpoint to the new one. A round aborts when a participant fails to respond.
Control messages
Coordinator
Participant
Channel state is captured in the state machines' waiting state: a process waiting for CHECKPOINT that receives a regular message logs it. Channels are FIFO, so a channel's CHECKPOINT arrives after everything its sender sent before stopping. When CHECKPOINT has arrived on every incoming channel, those channels are drained and their in-transit messages are in the log. The quiescent point does not mean “no messages in transit”, but “every in-transit message accounted for”.
Tamir & Sequin is blocking: normal execution is suspended during each round. The Chandy & Lamport distributed snapshot protocol [1985] is non-blocking: normal execution is never interrupted. But it is only concerned with how to produce a consistent global checkpoint. It prescribes no mechanism to determine the end of the round, nor to switch atomically to the new global checkpoint.
While Checkpointing, a regular message arriving on a channel whose Marker has not arrived yet is appended to that channel's state, and executed; after the Marker, messages are just executed. So a channel's state is the messages arriving after the process's own local checkpoint and before the Marker on that channel. The channel that brought the first Marker is empty: by FIFO, everything sent on it before the sender's checkpoint has already arrived. The state machine's final “report completion” is local; how the round ends is left open.
Analogies
Differences
Log-based protocols save events rather than state, and can recover to the state right before the failure, provided all events are logged and the log is available on recovery. They rest on an assumption:
Execution is modelled as consecutive state intervals, each started by a nondeterministic event (such as a message receipt) or by the process's initialisation, and followed by deterministic state changes. If the starting event is logged, the entire interval can be replayed.
Once the system emits an output to the outside world (e.g. its clients), part of its state becomes observable: a sort of commitment. Recovery must keep the outside view consistent, yet the outside world cannot be relied upon for recovery. This is the output commit problem: enough recovery information must be logged before the system commits to an output message.
Sorts of logging protocols [Alvisi & Marzullo, 1998]
Long story short: optimistic and causal logging must track dependencies and piggyback them on every message. That complicates logging and, above all, makes recovery sophisticated and expensive: the recovering process must examine its logs, find missing messages, and often trigger cascading recovery at other processes. Pessimistic logging is much simpler, and its recovery can be much faster.
In practice, logging is always used with checkpointing: states saved as checkpoints, events logged from one checkpoint to the next. Two benefits:
C2's lessons: dependability requires a coherent notion of dependencies and causality, and simple techniques like checkpointing and logging improve the chance of recovery.
Practise answering out loud, two minutes each.
Not necessarily. Availability is a probability at an instant, MTTF/(MTTF + MTTR); reliability covers an interval, R(Δt) = e−Δt/MTTF. Frequent failures with fast repair give high availability and low reliability. Say which one your users need.
A worker container is killed: a permanent crash fault (hardware or operator). The error is the lost state, its tasks in progress. It becomes a service failure only if it reaches the interface: the final result never arrives. For the cluster, the worker's failure is a fault: the chain of threats.
Local checkpoints must form a consistent global state: nothing received that was not sent (the $100 from nowhere). Independent checkpoints may never line up, so recovery needs recorded dependencies and may cascade. Show the domino run.
Both flood a control message and record channel state the same way. T&S blocks and uses 2PC, so the new global checkpoint is committed atomically or aborted. C-L never blocks and anyone can initiate, but ending the round and switching over are left to you.
Replay from the last checkpoint to just before the failure, if execution is piecewise deterministic and every nondeterministic event is logged; log before any output. Pessimistic logging costs latency but recovers simply; optimistic and causal cut latency but track dependencies and may cascade.
| Slides | Content | Here |
|---|---|---|
| M1 · Prologue | Premises I–IV: services, costs, engineers against downtime | §1 |
| M1 · Dependability & faults | Dictionary; basic concepts; system models I–V | §1 |
| M1 · Dependability & faults | Threat models I–III; sorts of faults (six criteria) | §2 |
| M1 · Dependability & faults | Failure models; fail-stop; fail-safe; fail fast | §2 |
| M1 · Attributes | Features [Tanenbaum & van Steen] | §3 |
| M1 · Attributes | Attributes [Zhao]; availability I–III; reliability; integrity; maintainability; safety | §3 |
| M1 · Means | Improving dependability; fault tolerance I–II; redundancy | §4 |
| M1 · Conclusion | Lessons learnt | §4 |
| C2 · Interaction… | Components; interaction; (inter)dependencies; causality I–V; time | §5 |
| C2 · Basic techniques | Basic mechanisms; checkpointing; recovery spoiler | §5 |
| C2 · Models | System model I–II; fault model; process state; stable storage | §5 |
| C2 · Models | Global state I–V (scenarios a, b, c); system model refined | §6 |
| C2 · Models | Piecewise deterministic assumption; output commit | §9 |
| C2 · Checkpoint-based | Generality; uncoordinated checkpointing; Tamir & Sequin I–VII | §7 |
| C2 · Checkpoint-based | Chandy & Lamport I–IV; discussion I–II | §8 |
| C2 · Log-based | Generality; sorts of logging; long story short; logging & checkpointing | §9 |
| C2 · Conclusion | Lessons learnt | §9 |