Your technical library
Testing & Verifying Distributed Systems
9 modules · 45 topics
About this book
Testing & Verifying Distributed Systems
Many engineers can explain Raft and name the isolation anomalies; far fewer can say what it would take to show that a system really is linearizable, why a test that passed for a week proves nothing about whether the system always eventually makes progress, or which of a specification, a fault-injection run, a deterministic simulator or a property test would have caught last quarter's outage. This course closes that gap. It answers one question from every side — how do you know a distributed system does what it claims? — for any engineer who will decide where a team spends its testing and verification effort, taught from nothing to a depth beyond what a senior staff engineer is expected to know. It builds on System Design, DBMS, Operating Systems, Data Structures, Algorithms and Mathematics.
What "correct" means states a claim precisely, as properties of a system's possible behaviours: safety (nothing bad ever happens) and liveness (something good eventually does). Specifying systems as state machines writes a design down so a tool can check it, refinement says when an implementation matches its specification, and model checking explores every reachable state of a small model. Checking histories asks the question after the fact: was this recorded execution linearizable, or serializable?
Then the implementation itself. Black-box fault testing drives a real cluster into failure, the way Jepsen — Kyle Kingsbury's testing project — does; deterministic simulation testing runs a whole cluster inside one reproducible process; and property-based testing and fuzzing generates inputs that find bugs no hand-written test reaches. Binding models to code connects the specification to what actually runs, and closes by choosing among the techniques by risk and cost.
45 topics across 9 modules, in dependency order.
This course is planned; no lessons are written yet. The map shows every module and topic so its coverage can be reviewed before authoring. Each topic will open with what to read first and close with what it leaves open.
Read at your own pace. Each topic is one continuous page; mark it done as you finish.
Start with 01.1 · Safety and liveness: every property is one, the other, or bothReview what you have finished↷ 0 skipped topics- 02.1Behaviours, states and actions: a system is the set of its behaviours
- 02.2Stuttering: why a specification must allow steps that change nothing
- 02.3Modelling the environment: the network, crashes and clocks as actions
- 02.4Atomicity grain: labels decide which interleavings exist
- 02.5Raft as a specification: what a real protocol spec models and abstracts
- 04.1Explicit-state model checking: BFS over every reachable state
- 04.2Checking liveness: lasso-shaped counterexamples and fairness
- 04.3State explosion: symmetry, partial-order reduction and bounding
- 04.4Symbolic and bounded checking: solvers instead of enumeration
- 04.5Random simulation: depth instead of breadth
- 06.1The Jepsen method: generators, faults, histories and a checker
- 06.2Designing faults: partitions, pauses, clocks, crashes and lost writes
- 06.3Choosing faults by lineage: which faults could break this outcome
- 06.4Storage faults in replicated systems: redundancy is not fault tolerance
- 06.5What fault testing keeps finding: the recurring failure classes
- 07.1Deterministic simulation: run the whole cluster in one seeded process
- 07.2Controlling nondeterminism: time, network, disk, scheduling and everything else
- 07.3Bug depth: a probabilistic guarantee for random scheduling
- 07.4Fault probability: why fixed rates miss bugs, and buggification
- 07.5Simulation oracles: invariants, reference models and liveness after healing
- 07.6Whole-system determinism: simulating unmodified software
- 08.1Properties and generators: the distribution is the test
- 08.2Shrinking: from a failing input to the smallest explanation
- 08.3Stateful and parallel property testing: a model as the oracle
- 08.4Stateless model checking: systematic schedules for real code
- 08.5Coverage-guided fuzzing: evolving inputs by feedback
- 08.6Fuzzing distributed systems: where coverage stops being a signal
- 09.1The spec–code gap: what verifying a design does not verify
- 09.2Trace validation: is this execution a behaviour of the spec?
- 09.3Model-based test generation: every spec behaviour as a test
- 09.4Executable models and runtime monitors: P and checking production logs
- 09.5Reference-model conformance: lightweight formal methods in S3's ShardStore
- 09.6Deductive verification: proving the implementation, and what remains trusted
- 09.7Choosing an assurance technique: risk, cost and what each can refute
01.2Inductive invariants: why a true invariant may be unprovable one step at a time
01.3Fairness: the assumption every liveness claim silently makes
01.4Consistency guarantees as properties: what a finite test can ever refute
02.1Behaviours, states and actions: a system is the set of its behaviours
02.2Stuttering: why a specification must allow steps that change nothing
02.3Modelling the environment: the network, crashes and clocks as actions
02.4Atomicity grain: labels decide which interleavings exist
02.5Raft as a specification: what a real protocol spec models and abstracts
03.1Refinement: implementation is implication
03.2When no mapping exists: history and prophecy variables
03.3A consensus log refines a register, until reads skip the log
04.1Explicit-state model checking: BFS over every reachable state
04.2Checking liveness: lasso-shaped counterexamples and fairness
04.3State explosion: symmetry, partial-order reduction and bounding
04.4Symbolic and bounded checking: solvers instead of enumeration
04.5Random simulation: depth instead of breadth
05.1Checking a history for linearizability: search with a sequential model
05.2Why linearizability checking is NP-complete, and how locality rescues it
05.3Isolation as forbidden cycles: Adya's dependency graphs
05.4Inferring anomalies from a black box: how Elle recovers the version order
06.1The Jepsen method: generators, faults, histories and a checker
06.2Designing faults: partitions, pauses, clocks, crashes and lost writes
06.3Choosing faults by lineage: which faults could break this outcome
06.4Storage faults in replicated systems: redundancy is not fault tolerance
06.5What fault testing keeps finding: the recurring failure classes
07.1Deterministic simulation: run the whole cluster in one seeded process
07.2Controlling nondeterminism: time, network, disk, scheduling and everything else
07.3Bug depth: a probabilistic guarantee for random scheduling
07.4Fault probability: why fixed rates miss bugs, and buggification
07.5Simulation oracles: invariants, reference models and liveness after healing
07.6Whole-system determinism: simulating unmodified software
08.1Properties and generators: the distribution is the test
08.2Shrinking: from a failing input to the smallest explanation
08.3Stateful and parallel property testing: a model as the oracle
08.4Stateless model checking: systematic schedules for real code
08.5Coverage-guided fuzzing: evolving inputs by feedback
08.6Fuzzing distributed systems: where coverage stops being a signal
09.1The spec–code gap: what verifying a design does not verify
09.2Trace validation: is this execution a behaviour of the spec?
09.3Model-based test generation: every spec behaviour as a test
09.4Executable models and runtime monitors: P and checking production logs
09.5Reference-model conformance: lightweight formal methods in S3's ShardStore
09.6Deductive verification: proving the implementation, and what remains trusted
09.7Choosing an assurance technique: risk, cost and what each can refute
Skipped topics
Moved past without finishing. A skipped topic still closes its module, and Continue goes on without it — open one to read it or mark it understood.