Skip to content

Fix flaky Task.WhenAny + Assert.Same timing races - #246

Merged
pedrosakuma merged 3 commits into
mainfrom
fix-245-flaky-async-tests
Aug 29, 2026
Merged

Fix flaky Task.WhenAny + Assert.Same timing races#246
pedrosakuma merged 3 commits into
mainfrom
fix-245-flaky-async-tests

Conversation

@pedrosakuma

Copy link
Copy Markdown
Owner

Fixes #245.

Adds an AsyncAssert.CompletesWithinAsync helper (one copy per test
project, following the existing Infrastructure/ convention already
used in the Conformance suite) to replace the fragile
Assert.Same(task, await Task.WhenAny(task, Task.Delay(timeout)))
pattern flagged in the issue. The helper:

  • reports elapsed time and an optional "because" reason on timeout,
    instead of the opaque Assert.Same() Failure: Values are not the same instance;
  • re-awaits the original task after the race so any exception it
    faulted with propagates, rather than being silently swallowed by
    Task.WhenAny.

Also widened the fixed timeouts called out in the issue with modest
headroom for CI-runner contention:

  • KeepAliveSchedulerTests: 5s → 10s (40ms tick interval kept as-is,
    cleanup moved into a finally so Stop()/Dispose() still run on
    timeout)
  • SequenceHeartbeatTests: 5× → 10× interval
  • RetransmitTests / RetransmitRejectTests / NotAppliedTests /
    ReconnectRetransmitTests: 3s → 5s

All 6 call sites named in the issue (plus the one in
SequenceHeartbeatTests not explicitly listed) were converted.

Validation

  • dotnet build SbeB3EntryPointClient.slnx — succeeded, 0 warnings.
  • dotnet test SbeB3EntryPointClient.slnx --no-build — 233/233 passed
    in B3.EntryPoint.Client.Tests; Conformance suite skipped (no peer
    configured locally), consistent with existing CI behavior.
  • dotnet format SbeB3EntryPointClient.slnx --verify-no-changes --no-restore --severity warn — no changes needed.

Copilot and others added 3 commits August 29, 2026 14:17
Add an AsyncAssert.CompletesWithinAsync helper (one copy per test
project, matching the existing Infrastructure/ convention in the
Conformance suite) to replace the fragile
`Assert.Same(task, await Task.WhenAny(task, Task.Delay(timeout)))`
pattern. The helper:

- reports elapsed time and a "because" reason on timeout, instead of a
  bare "Values are not the same instance" failure;
- re-awaits the original task so any exception it faulted with
  propagates, rather than being swallowed by Task.WhenAny.

Widened the fixed timeouts flagged in the issue with modest headroom
for CI-runner contention (KeepAliveSchedulerTests 5s -> 10s,
SequenceHeartbeatTests 5x -> 10x interval, Retransmit/NotApplied/
ReconnectRetransmit 3s -> 5s), and moved KeepAliveSchedulerTests'
scheduler Stop()/Dispose() into a finally block so cleanup still runs
if the wait times out.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI on PR #246 still failed with the widened 5s timeout, and the
elapsed time was exactly 5.0002030s — i.e. a real hang, not a
close-shave flake. The TryPeek-then-subscribe fallback left a window
between checking peerInboundNosSeqs and subscribing the probe handler
where the peer's MessageReceived event for the resubmitted order could
fire and be lost forever, since nothing was listening for it yet and
peerInboundNosSeqs was checked (not re-checked) only once.

Fix: subscribe the probe before calling SubmitAsync so the event
literally cannot be missed. Ran the affected test 20x locally against
the in-process TestPeer with no failures (previously it flaked here
under contention).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@pedrosakuma
pedrosakuma merged commit e5f98af into main Aug 29, 2026
6 checks passed
@pedrosakuma
pedrosakuma deleted the fix-245-flaky-async-tests branch August 29, 2026 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flaky tests: Task.WhenAny + Assert.Same timing races fail intermittently in CI

1 participant