4.5 KiB
4.5 KiB
T7.7 — turmoil suite
| Field | Value |
|---|---|
| Phase | P7 — Distribution |
| Size | L — over 3 days |
| Status | Not started |
| Flags | parallel-ok |
| Spec | inlined below |
| Blocks | — |
Goal
Network partition, latency and node loss simulated deterministically. This is the P7 gate.
Facts (inlined — no spec read needed)
- Test tooling and what each covers:
turmoil— network partition and latency simulation.loom— the lock-free bits.tokio::time::pause— time.
- Together these are weaker than a seeded scheduler and sufficient with
discipline. The residual risk is stated honestly rather than engineered
around: tokio's cancellation is cooperative, so a
select!-dropped future stops at its next await and not before. - What the suite must prove: no double-execution and no lost run under partition, with automatic recovery.
- Double-execution is prevented by fencing (T7.3), not by the partition never happening. This suite is what demonstrates the fence actually fires.
parallel-ok: this task does not gate others and can be built alongside the rest of P7.- Open: whether a lint, a wrapper type, or a
loomharness is the right enforcement for cancellation rigour under tokio. Unresolved — do not invent a fourth mechanism here.
Steps
- Stand up a two-node topology under
turmoil, both nodes claiming runs from the same Postgres backend. - Partition scenario: isolate node A mid-run, let its lease expire, watch node B reclaim, then heal the partition and assert node A's writes are fenced off.
- Latency scenario: inject delays that push heartbeats close to the TTL, and assert renewals either succeed or cancel the local scope — never a silent continue.
- Node-loss scenario: kill a node outright; assert the run is reclaimed and completes exactly once.
- Add
loomcoverage for any lock-free structure introduced in P7. - Use
tokio::time::pausefor all TTL and deadline arithmetic so the suite runs in seconds. - Print per-scenario progress and enforce per-scenario timeouts — a suite that prints nothing cannot distinguish slow from hung.
Acceptance
- No double-execution and no lost run under partition; recovery is automatic.
Phase gate
P7 closes on two nodes surviving a partition with no double-execution.
Verify
Harness: turmoil two-node topology; loom for any lock-free structure;
tokio::time::pause for TTL arithmetic; the external side-effect ledger as the
double-execution witness.
Integration test — tests/it_turmoil.rs:
- Partition: isolate node A mid-run. Assert node B reclaims after lease expiry, the run completes, and the ledger shows exactly one effect per intended effect.
- Heal the partition; assert A's writes are fenced (cross-check T7.3) and that A does not resume executing.
- Latency: inject delays that push heartbeats close to the TTL. Assert renewals either succeed or cancel the local scope — assert no case where a worker continues past a failed renewal.
- Node loss: kill a node outright. Assert the run is reclaimed and completes exactly once.
- Assert no lost run: every spawned run reaches a terminal state across all scenarios; count spawned vs terminal.
- Recovery is automatic — assert no manual intervention hook was invoked.
loomtest for each lock-free structure added in P7.- Per-scenario progress output and per-scenario timeouts.
- Print the seed; a failure that cannot be replayed is not a finding.
Command: cargo test -p distribution --test it_turmoil -- --nocapture
False pass:
- Asserting the run completed. Completing twice also completes — the ledger count is the only assertion that distinguishes them, and double-execution is the primary risk this suite exists to retire.
- Real
sleepfor TTL expiry, making the suite slow enough that it stops being run — which is the same as not having it. - Step 5 omitted: a run that is silently dropped during a partition leaves no error, and every other assertion passes.
- Testing only the partition scenario. Node loss and slow-network cases exercise different reclaim paths.
Traps
- Asserting only that the run completes. Completing twice also completes.
- Real sleeps for TTL expiry, which makes the suite too slow to run and therefore unrun.
Background (not required to do this task): rust-agentic-sys.md §6, §9.4, §18, §19 · rust-agentic-task.md