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:

flagdefaultmeaning
--seed N0splitmix64 seed mixed into election timers and message delays
--nodes K3number of Raft nodes (1 is legal; majority is then 1)
--rounds R1000number of simulator ticks to run
--proposals P0number of client commands to inject during the run
--partition s,d,...nonecomma-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.

labelargs
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