guard: make the stopped run hole certain instead of raced for - #90
Merged
Conversation
TestARunStoppedPartWayNamesEveryFileThatFinished needs a run where one writer is still owed bytes while its neighbours have been renamed into place. It bought that state with size - a first file sixteen times the size of the rest - and that made it flaky, roughly one run in four inside a full suite. Size buys margin in work, and which writer finishes first is decided by which one gets a processor. Measured with tools/probes/stoprace, fifty runs per condition: idle machine 0 failures in 50, 165 ms of unused margin heavy disk writing beside it 0 failures in 25 CPU starved, 24 busy loops 14 failures in 25 So the disk was never involved, which is what the observation first recorded. Under starvation the time to the first finished file goes from 11 ms to 5.8 s, and sixteen times the work stops meaning anything. A full suite runs packages beside each other and builds binaries in sub processes, so a starved machine is the normal condition there. The file at index zero now carries a generator that writes nothing and returns only once the run is cancelled, so it cannot finish whoever gets a processor. Descriptor is a value and every planned file carries its own copy, so this is installed after planning on one PlannedFile - the registry is untouched, the other thirty one files write real bytes, and every step of the engine below Plan is the one that ships. The guard also asserts it has at least two writers, because with one the held open file would wait for a cancellation nobody can send. Measured after the change: 25 of 25 green under the same starvation, the mutation that restores the prefix behaviour still reddens it, and the guard writes 3.5 MiB where it used to write 94 MiB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
TestARunStoppedPartWayNamesEveryFileThatFinishedwas flaky, roughly one run in four inside a full suite. It needs a run where one writer is still owed bytes while its neighbours have already been renamed into place, and it bought that state with size - a first file sixteen times the size of the rest.The recorded diagnosis was wrong
The observation said the flakiness depended on how fast the machine writes. Measured with a new probe, fifty runs per condition:
The disk is not involved. Size buys margin in work, and which writer finishes first is decided by which one gets a processor. Under starvation the time to the first finished file goes from 11 ms to 5.8 s - three orders of magnitude - at which point sixteen times the work means nothing. A full suite runs packages beside each other and builds binaries in sub processes, so a starved machine is the normal condition there rather than an exotic one.
The fix removes the race rather than widening it
The file at index zero now carries a generator that writes nothing and returns only once the run is cancelled, so it cannot finish no matter who gets a processor.
Descriptoris a value and every planned file carries its own copy, so this is installed after planning, on onePlannedFile. The registry is untouched, the other thirty one files write real bytes through the real generator, and every step of the engine belowPlanis the one that ships.The guard also asserts it has at least two writers, because with one the held open file would wait for a cancellation nobody can send. A deadline in the generator turns a hypothetical deadlock into a sentence naming the guard that caused it, rather than a hung package.
Verified
-raceclean over 5 runs,staticcheckv0.8.1 clean,gofmtandvetcleansafety_test.gogreen, all fifteen code shape ceilings green, document consistency guards green,staleness.pyreports every pattern occurring exactly onceThe five guards in
progress_test.gowere measured under the same starvation as well - 50 of 50 green, because none of them cancels a run or assumes who finishes first.Not run: the full suite and preflight, deliberately - this goes to CI.
🤖 Generated with Claude Code