docs/internals/SIMULATION_FIRST_WORKFLOW.md
This document defines the mandatory workflow for all Quickwit development, following the verification pyramid philosophy.
^
/|\
/ | \
/ | \ TLA+ Specs (docs/internals/specs/tla/)
/ | \ - Mathematical model
/----+----\ - Defines "what is correct"
/ | \
/ | \ Stateright Models
/ | \ - Rust-native model checking
/--------+--------\- Verifies state space
/ | \
/ | \ DST Tests
/ | \- Deterministic simulation
/------------+------------\- Fault injection
/ | \
/ | \ Unit/Integration Tests
/ | \- Fast feedback
/----------------+----------------\
/ | \
/ | \ Production Monitoring
/-------------------+-------------------\- production invariant metrics
Check existing TLA+ spec in docs/internals/specs/tla/
Check existing Stateright model
Write DST tests
#[test]
fn test_feature_invariant_holds() {
let config = SimConfig::new(SEED);
let mut sim = Simulation::new(config);
sim.run(|env| async move {
// Setup
let component = create_component();
// Exercise (with fault injection)
for _ in 0..iterations {
perform_operation(&component).await?;
}
// Verify invariants from TLA+ spec
verify_invariant_1(&component)?; // Maps to TLA+ line X
verify_invariant_2(&component)?; // Maps to TLA+ line Y
Ok(())
});
}
Run tests - EXPECT FAILURE
cargo test -p quickwit-dst -- your_feature_tests
# Should fail: component doesn't exist yet
Write minimal implementation to make DST tests pass
debug_assert! for invariants that match TLA+ propertiesRun tests - EXPECT PASS
cargo test -p quickwit-dst -- your_feature_tests
# Should pass now
Run Stateright model (if applicable)
cargo test -p quickwit-dst -- stateright_your_feature
Run unit tests
cargo nextest run -p your-crate -- your_feature
Run integration tests
# Rust integration tests
cargo nextest run -p quickwit-integration-tests
# REST API tests (if touching API surface)
cd rest-api-tests && ./run_tests.py --engine quickwit
Full verification
cargo nextest run --all-features
cargo clippy --workspace --all-features --tests
1. Write implementation
2. Create PR
3. "Oh, should I write tests?" (asked by reviewer)
4. Write tests after the fact
1. Read TLA+ spec for invariants
2. Write DST test that verifies invariant
3. Run test -> FAILS (no implementation)
4. Write implementation
5. Run test -> PASSES
6. Run Stateright -> PASSES
7. Run unit + integration tests -> PASSES
8. Create PR with test evidence
Even when skipping DST, still write tests before implementation when practical.
Quickwit uses an actor framework (quickwit-actors) for concurrent components. When applying simulation-first to actors:
// Example: Testing an actor with DST
#[test]
fn test_indexer_actor_handles_storage_fault() {
let config = SimConfig::new(SEED);
let mut sim = Simulation::new(config)
.with_fault(FaultConfig::new(FaultType::StorageWriteFail, 0.1));
sim.run(|env| async move {
let indexer = IndexerActor::new(env.storage());
// Send messages through the mailbox
indexer.send(IndexMessage::IndexBatch(batch)).await?;
// Verify invariants hold despite faults
assert!(indexer.state().splits_published >= expected_min);
assert!(indexer.state().no_data_loss());
Ok(())
});
}
cargo clippy --workspace --all-features --tests passescargo +nightly fmt -- --check passes