db-17 — Execution
One-shot: prove the lab works
cd db-17-raft
./scripts/verify.sh # all unit tests in Rust, Go, C++
./scripts/cross_test.sh # byte-identical sha256 across all three, six scenarios
A green run of cross_test.sh ends with the literal line:
=== ALL OK ===
Per-language workflows
Rust
cd src/rust
cargo test --release # ~10 tests
cargo build --release # produces target/release/raftctl
./target/release/raftctl --seed 42 --nodes 3 --rounds 1000 --proposals 5
Go
cd src/go
go test ./... # ~12 tests
go build -o /tmp/raftctl_go ./cmd/raftctl
/tmp/raftctl_go --seed 42 --nodes 3 --rounds 1000 --proposals 5
C++
cd src/cpp
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build # test_db17 → "db-17 C++ tests: 10 passed"
./build/raftctl --seed 42 --nodes 3 --rounds 1000 --proposals 5
CLI
All three binaries accept the same flags and print lowercase hex sha256 of the canonical dump to stdout with no trailing newline:
| flag | default | meaning |
|---|---|---|
--seed N | 0 | splitmix64 seed mixed into election timers and message delays |
--nodes K | 3 | number of Raft nodes (1 is legal; majority is then 1) |
--rounds R | 1000 | number of simulator ticks to run |
--proposals P | 0 | number of client commands to inject during the run |
--partition s,d,... | none | comma-separated pairs (src, dst) to drop in that direction |
--partition 0,1,1,0 drops both directions between nodes 0 and 1
(complete split); --partition 0,1 drops only 0 → 1 (asymmetric).
Proposals are spaced as schedule[i] = (i+1) * rounds / (K+1); with
--rounds 1000 --proposals 5 they fire at ticks 166, 333, 500, 666,
833.
Canonical scenarios
scripts/cross_test.sh runs six scenarios; their sha256s are listed in
docs/observation.md. If any change, cross_test.sh will diff the
raw dumps and exit non-zero.
| label | args |
|---|---|
| A | --seed 42 --nodes 3 --rounds 1000 --proposals 5 |
| B | --seed 7 --nodes 5 --rounds 2000 --proposals 20 |
| C | --seed 99 --nodes 3 --rounds 500 --proposals 0 |
| D | --seed 1 --nodes 1 --rounds 200 --proposals 5 |
| E | --seed 42 --nodes 3 --rounds 1000 --proposals 3 --partition 0,1,0,2,1,0,2,0 |
| F | --seed 3 --nodes 5 --rounds 1500 --proposals 10 --partition 0,1 |
D exercises the single-node-leader code path that motivated the
propose() → advance_commit() call. E isolates node 0 completely; the
other two must elect a leader and commit the remaining proposals. F is
an asymmetric partition that causes term churn but recoverable
replication.
Sanity checks
# magic bytes of the canonical dump (use the lib directly; the CLI hashes it)
cat <<'EOF' | cargo run --quiet --example dump_magic
EOF
# or just trust the test: TestCanonicalDumpMagic in raft_test.go
# or for C++: test_db17 prints "canonical dump magic OK" among its asserts
# pick any scenario and round-trip:
./src/rust/target/release/raftctl --seed 42 --nodes 3 --rounds 1000 --proposals 5
# expect: a2299ff06a2ed5ced5842d100bb7867b3ae50f6e7d7da93f835385565f1ed9e9