test: harden G4 snapshot sampler against flake - #13
Merged
Merged
Conversation
1.producer may finish first -> gate on start 2.racy samples>0 guard -> sample-first loop 3.busy-poll burned CPU -> yield in sampler
There was a problem hiding this comment.
Pull request overview
This PR hardens the RingQueue G4 concurrency test to eliminate a scheduler-ordering flake that caused intermittent failures in the weekly -race -count=500 stress runs (Issue #12). Production code remains unchanged; only the test behavior and scheduling determinism are adjusted.
Changes:
- Add a start barrier so the producer can’t finish enqueues before the sampler takes its first snapshot.
- Replace the prior
select { ... default: ... }liveness guard with a sample-first loop that always snapshots. - Add
runtime.Gosched()to avoid busy-polling and improve interleaving between sampler/producer/consumer.
1.barrier wait ignored ctx -> add ctx.Done case 2.match consumer ctx handling -> symmetric shutdown
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.
Summary
TestRingQueue_G4_ConcurrentEnqueueAndSnapshot, the sole cause of the weekly-race -count=500stress failures (Issue Race condition detected — 2026-07-02 #12). It was a test bug, not a data race and not aSnapshotdefect — the race detector reported nothing.startchannel released after the sampler's first snapshot, so the enqueue/snapshot ordering is deterministic instead of scheduler-dependent.assert.Greater(samples, 0)liveness guard (the sample-first loop now guarantees at least one snapshot) and addruntime.Gosched()so the sampler no longer busy-polls and starves the producer/consumer.Related issues
Closes #12
Motivation
The weekly stress workflow (
-race -count=500) intermittently failed and auto-opened Issue #12 with arace-conditionlabel, but the race detector reported nothing. The real failure was a liveness assertion —"0" is not greater than "0"/ "sampler should have observed at least one snapshot".Go's
selectnever choosesdefaultwhen another case is ready. When the producer's 500 non-blockingForceEnqueuecalls closedproducerDonebefore the sampler goroutine reached its firstselect, the sampling branch never ran, sosamplesstayed0andassert.Greater(t, samples, 0)failed. This is a scheduler-ordering race in the test, not a wall-clock issue and not a production concurrency bug:RingQueue.SnapshotandForceEnqueueboth holdq.mufor the whole operation and return a copy.Changes
test: hardenTestRingQueue_G4_ConcurrentEnqueueAndSnapshotstartbarrier released after the first snapshot.select-defaultliveness guard with a sample-first loop.runtime.Gosched()so the sampler interleaves with the producer and consumer instead of busy-polling.Test plan
"0" is not greater than "0"deterministically, matching the CI failure.TestRingQueue_G4_ConcurrentEnqueueAndSnapshotpassesGOMAXPROCS=1 -race -count=2000(the regime that most aggressively reproduces the flake). TheGoschedyield also cut that run from ~98s to ~2s.TestRingQueuetests pass at-race -count=300.make check(fmt + lint + race test) passes..gofile changed.Checklist
Required
make check)Notes
startbarrier makes the liveness deterministic; the enqueue/snapshot interleaving stays intentionally nondeterministic (the point of a concurrency stress test). Ordering correctness is covered deterministically by G6.testing/synctest(needs Go 1.24+; module floor isgo 1.22.0) and an explicittime.After/context.WithTimeoutguard (would reintroduce a wall-clock timing dependency — the exact anti-pattern this fix removes).race-conditioneven when it is not; retuning that auto-issue labeling is a possible follow-up.