Correctness + Load Harness (CONCEPT:AU-KG.ontology.emits-database-ontology-entities)¶
The standing proof-engine that gates every distributed / durability claim in the Waves-4-8 program: multi-Raft (lane A), M2 redb durability, distributed transactions (lane N), PITR / replication (lane E). The authoritative-flip migration deadlock proved correctness-tests-passing ≠ scale/fault-proven; this harness exists so later distributed work must survive injected faults, not just pass happy-path unit tests.
It is test/dev-only — gated behind the harness cargo feature (which pulls
raft). A production build (cargo build, --features full, --features cluster) links
nothing from it, and the one production-code touch (a partition gate in the Raft
network) is #[cfg(any(test, feature = "harness"))], so the production network path
is byte-for-byte unchanged.
Where it lives¶
src/raft/harness/
├── mod.rs # run_gauntlet() — elect → load → fault → heal → assert
├── cluster.rs # Cluster: managed N-node in-process redb-authoritative raft
│ # cluster with kill / restart / partition / read controls
├── nemesis.rs # Nemesis: the 4 fault primitives, schedulable + seeded
├── loadgen.rs # concurrent writers → recorded History
├── history.rs # the (op, ack/err, timestamp) op log
├── checker.rs # the invariant model over history + final state
└── gauntlet_test.rs # `cargo test --features harness raft::harness` — the gauntlet
src/bin/nemesis.rs # `cargo run --features harness --bin nemesis` — the soak runner
src/raft/network.rs # partition::{partition,isolate,heal,reachable} gate (cfg-gated)
The nemesis (4 fault primitives)¶
| Fault | What it does | Mechanism |
|---|---|---|
Kill (KillLeader/KillNode) |
kill -9 a node mid-write |
Cluster::kill drops the node's Raft + redb backend; the on-disk redb is exactly what a crashed process leaves (every acked write was fsynced before the ack). restart re-opens over the same files → local log replay (EG-KG.storage.one-fsync-covers-raft) + catch-up. |
| Partition | Drop the group-tagged TCP frames between subsets | A process-global partition "islands" controller the Raft network's per-RPC round_trip consults; a partitioned pair gets a ConnectionReset (the same thing a real dropped frame surfaces) → openraft Unreachable → backoff. |
Skew / Pause (Isolate) |
A clock-skewed / GC-paused node stops heartbeating | Modeled as a network isolation window: peers see missed heartbeats → election. (Real monotonic-clock skew of one node is deferred — see below.) |
| Saturate | Flood the write channel | The load gen's rate_per_sec = 0 burst contends for the leader's group-commit + in-flight semaphore; SaturateWindow marks the window. |
All randomized faults keep a quorum (minority partition / one-node kill), so the no-loss bar stays meaningful (writes can still commit during the fault).
The load generator¶
loadgen::run spawns writers tasks issuing durable AddNode mutations through the
cluster's current leader (re-resolved each op, so a failover just reroutes the next
write) at a configurable rate. Each op gets a globally-unique seq and is recorded as
an Op { seq, writer, invoked, completed, outcome: Acked | Err(_) } in a shared
History.
The checker — invariants asserted¶
Over the recorded History + the cluster's final state (after heal + a stable leader):
- Acked-write-survives / no-loss. Every
seqthe client observedAckedis present on the final authority (the healed leader). An acked-then-lost write is the headline durability bug → FAIL loudly. - Single-leader-per-term (no split-brain). A background sampler records every
node's
(id, term, is_leader); no two distinct nodes may be leader of the same term. - Monotonic commit / no-phantom.
acked ⊆ applied ⊆ issued— the authority holds no entry that was never issued, and its applied count never exceeds issued writes.
Full Knossos-style linearizability (per-key register history + a real-time order check) is a later increment; this asserts the weaker but high-value set above.
Running it¶
# Bounded, seeded, CI-able gauntlet (partition → heal → kill-leader → assert):
cargo test --features harness --lib raft::harness
# Longer seeded soak (random quorum-preserving faults, N rounds, fail-fast):
cargo run --features harness --bin nemesis -- --nodes 3 --rounds 20 --seed 42
How a future distributed lane plugs in¶
- A new invariant (lane N cross-shard atomicity; lane E "PITR recovers to T"):
add a check arm in
checker::checkreading the relevant final state, plus a run-time accumulator likechecker::LeadershipLogif it needs samples. The load gen already records a full timestamped history to assert against. - A new nemesis (lane resharding: split a group mid-write; lane E: kill a
replication follower): add a
nemesis::Faultvariant + aninjectarm. The partition gate and the kill/restart cluster controls are reusable primitives. - A new gauntlet: compose
Cluster::start→loadgen::run→Nemesis::inject→checker::checkexactly asrun_gauntletdoes, with the new fault schedule.
Deliberately deferred (documented)¶
- Full linearizability (Knossos-style real-time-order model checker).
- Real monotonic-clock skew of one node (modeled today as network isolation — the observable effect on peers is identical: missed heartbeats).
- Multi-process (not in-process) nodes — a true
kill -9of a separate OS process. The in-process kill drops the node's Raft + redb backend, leaving the on-disk redb a crash would leave (durability is the property under test, and that holds).
Production-isolation guarantee¶
cargo tree -e features -i openraft (default build) etc. and the harness feature being
absent from the main/cluster builds keep the harness out of shipped builds. The one
production-source touch — GroupNetworkClient::round_trip's partition check — is
#[cfg(any(test, feature = "harness"))]; in a production build it is not compiled, so
the Raft network path is unchanged.