Skip to main content
KBY Technologies Logo
cd ../lexicon
sys/docs/lexicon/deterministic-simulation-testing-dst.md
Lexicon Entry

Deterministic Simulation Testing (DST)

A testing approach that runs an entire distributed system inside a single-threaded, seeded simulation so that any failure, however rare, can be reproduced exactly by replaying the same seed.

Practical example

FoundationDB's Flow simulator drives its actor-model runtime through millions of seeded runs, injecting network partitions, disk bit-rot and clock skew, compressing years of real-world fault exposure into hours of CI time and capturing any invariant violation as a single reproducible seed.

DST inverts the typical distributed systems testing model. Instead of running real processes on real threads communicating over real sockets (introducing genuine OS-level nondeterminism), the entire system-under-test is compiled or linked against a simulated runtime. This runtime intercepts all sources of nondeterminism: wall-clock time, thread scheduling, disk fsync ordering, and network delivery. A single logical thread drives a discrete-event simulation loop, and every ‘random’ decision—message delay, packet drop, node crash, clock skew—is derived from a PRNG seeded once at the start of the run. The core invariant is: same seed == same execution trace, every time.

The architectural cost is significant: production code must be written against an abstracted I/O layer (a ‘deterministic executor’), rather than calling time.Now(), spawning OS threads, or hitting the syscall layer directly. FoundationDB pioneered this pattern by building its entire actor model (Flow) around a simulator that could compress years of fault injection into hours, discovering bugs that would statistically never surface in conventional integration tests. TigerBeetle and later systems adopted similar patterns (VOPR-style simulators) specifically because Jepsen-class testing, while excellent at generating a linearizability history, cannot guarantee reproducibility of the exact byte sequence that triggered a bug.

  • Fault injection as a first-class citizen: the simulator doesn’t just delay packets—it can partition arbitrary subsets of the cluster, inject bit-rot into disk blocks, truncate WAL segments mid-write, and simulate clock drift, all driven by the same seed.
  • Swarm testing: running millions of seeds in parallel on CI infrastructure to hill-climb toward rare failure states (e.g. a 3-way network partition coinciding with a leader crash during a specific WAL fsync).
  • Buggy seed capture: when an invariant check (e.g. no lost writes, no split-brain) fails, the seed and the initial configuration are the entire bug report—no logs, no distributed tracing needed to reproduce it locally.

The critical edge case engineers must respect is determinism leakage: any accidental dependency on real wall-clock time, map iteration order, hash randomization, or floating-point non-associativity across platforms will silently break reproducibility, turning DST into an expensive flaky-test generator. Because of this, systems built for DST typically forbid direct syscalls in business logic entirely, funneling all I/O through an injectable interface—this discipline itself becomes a forcing function for cleaner, more testable distributed system architecture, independent of the testing payoff.