Deterministic simulation of multi-worker computations - #810
Open
frankmcsherry wants to merge 2 commits into
Open
Deterministic simulation of multi-worker computations#810frankmcsherry wants to merge 2 commits into
frankmcsherry wants to merge 2 commits into
Conversation
This was referenced Jul 15, 2026
frankmcsherry
force-pushed
the
simulation
branch
from
September 3, 2026 21:14
ce0188a to
0ecf363
Compare
A `Gate` sits on the receive side of the serializing allocators, `ProcessAllocator` and `TcpAllocator`. It physically receives all bytes. Built open, the default, bytes pass through as before. Built holding, it splits received bytes into framed messages, files them by source worker, and surfaces a message only when released. Held messages raise no events, so operators see only what has been logically delivered. Per-source FIFO order is preserved; the holder chooses only the interleaving across sources. Messages a worker sends to itself are never held, matching real allocators. Whether a gate holds is a builder setting, `ProcessBuilder::holding`, fixed when the allocator is built. Building several `Bytes` process allocators sequentially on one thread deadlocked, because each `build` waits on its peers. `ProcessBuilder::build_all` fulfills every obligation before completing any allocator. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEpSYjkqw2zfhGSMAXNXyD
Adds `timely::simulate`: several workers hosted on the calling thread, using ordinary `Bytes` process allocators with holding gates. An execution is a pure function of the applied sequence of `Decision`s: step a worker, or deliver up to N messages from one worker to another. Any decision sequence is valid, since deliveries clamp to what is pending, so schedule traces are recordable, replayable, and shrinkable by construction. Tests run multi-worker barriers under seeded random schedules, asserting progress safety. A property-testing grind checks frontier safety, conservation, quiescence, and same-seed determinism. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEpSYjkqw2zfhGSMAXNXyD
frankmcsherry
force-pushed
the
simulation
branch
from
September 4, 2026 13:54
0ecf363 to
331d472
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Deterministic simulation of multi-worker computations
Adds
timely::simulate: several workers hosted on the calling thread, with message delivery controlled by the caller.An execution is a pure function of the applied sequence of
Decisions (step a worker; deliver up to N messages from one worker to another).Any decision sequence is valid, since deliveries clamp to what is pending, so schedule traces are recordable, replayable, and shrinkable by construction.
Design
Delivery control lives in the existing serializing allocators rather than in a separate simulation allocator.
A
Gatesits on the receive side ofProcessAllocatorandTcpAllocator.It physically receives all bytes.
When built open, the default, bytes pass through as before.
When built holding, it splits received bytes into framed messages, files them by source worker, and surfaces a message only when released.
Held messages raise no events, so operators see only what has been logically delivered.
Per-source FIFO order is preserved; the driver chooses only the interleaving across sources, which is the schedule space a real transport can produce.
Messages a worker sends to itself are never held, matching real allocators.
Whether a gate holds is a builder setting,
ProcessBuilder::holding, fixed when the allocator is built.Simulationbuilds ordinaryBytesprocess allocators on one thread with holding gates.Building those sequentially on one thread deadlocked, because each
buildwaits on its peers, soProcessBuilder::build_allfulfills every obligation before completing any allocator.Commits:
Gate, theholdingbuilder setting, andbuild_all.Simulation/Decisiondriver and tests: multi-worker barriers asserting progress safety under seeded random schedules, and a property-testing grind whose oracles check frontier safety, conservation, quiescence, and same-seed determinism.Why this is worth carrying in-tree
This harness produced the deterministic reproduction of TimelyDataflow/differential-dataflow#801, a batch-loss bug in DD's join that had resisted reproduction except as a ~20% flake under CPU contention with three workers.
Under the simulator the bug reproduced on a single worker with no chaos at all, deterministically on every seed, which made the bisection to first divergence (and the fix, differential-dataflow#802) tractable.
The follow-up plan is a DD regression test running the #801 computation under seeded schedules against a sequential oracle, once this is released.
Validation
#[ignore]dchaos_big_grindat 2,000 runs.🤖 Generated with Claude Code
https://claude.ai/code/session_01LEpSYjkqw2zfhGSMAXNXyD