fix(gameserver): drain the simulation queue on shutdown to avoid a hang #62

Merged
marco merged 1 commit from fix/simulation-shutdown-hang into main 2026-07-19 07:03:50 +02:00
Owner

A pre-existing flaky hang in CI (surfaced on #61, but unrelated to its content — #61 only touches the extractor + pack, and #60 with identical test code passed).

Root cause

Simulation.InvokeAsync writes work with TryWrite, which succeeds until the queue is completed. During shutdown there is a race: work written after the reader loop exits (on cancellation) but before the queue is completed sits undrained forever, so its awaiter — e.g. a disconnecting WebSocket handler mid-InvokeAsync while the test host disposes — hangs indefinitely. Rare locally (tests pass in ~6s), but a slow/contended CI runner widens the race window and stalls the whole GameServer.Tests job (the "stuck 6 min" symptom).

Fix

RunAsync's finally now (1) completes the queue first, so any later InvokeAsync faults fast instead of hanging, then (2) drains and runs whatever remained, so every awaiter settles. Regression test floods InvokeAsync across cancellation and asserts all calls settle within a 5s guard.

just lint -> 0 warnings. just test -> GameServer 139 green (6s).

Definition of Done

  • No posted work can be left undrained on shutdown; its awaiter always settles (completes or faults).
  • Regression test reproduces the shutdown race and passes with the fix.
  • Whole solution builds, zero warnings, tests green.
A **pre-existing flaky hang in CI** (surfaced on #61, but unrelated to its content — #61 only touches the extractor + pack, and #60 with identical test code passed). ## Root cause `Simulation.InvokeAsync` writes work with `TryWrite`, which succeeds until the queue is completed. During shutdown there is a race: work written **after** the reader loop exits (on cancellation) but **before** the queue is completed sits **undrained forever**, so its awaiter — e.g. a disconnecting WebSocket handler mid-`InvokeAsync` while the test host disposes — **hangs indefinitely**. Rare locally (tests pass in ~6s), but a slow/contended CI runner widens the race window and stalls the whole `GameServer.Tests` job (the "stuck 6 min" symptom). ## Fix `RunAsync`'s `finally` now (1) completes the queue first, so any later `InvokeAsync` faults fast instead of hanging, then (2) drains and runs whatever remained, so every awaiter settles. Regression test floods `InvokeAsync` across cancellation and asserts all calls settle within a 5s guard. `just lint` -> 0 warnings. `just test` -> GameServer 139 green (6s). ## Definition of Done - [x] No posted work can be left undrained on shutdown; its awaiter always settles (completes or faults). - [x] Regression test reproduces the shutdown race and passes with the fix. - [x] Whole solution builds, zero warnings, tests green.
fix(gameserver): drain the simulation queue on shutdown to avoid a hang
All checks were successful
ci / Lint & Test (pull_request) Successful in 1m44s
1818dd22d9
InvokeAsync writes work to the queue with TryWrite, which succeeds until the
queue is completed. During shutdown there was a race: work written after the
reader loop exited (on cancellation) but before the queue was completed sat
undrained forever, so its awaiter — e.g. a disconnecting handler mid-InvokeAsync
while the test host disposes — hung indefinitely. Rare locally, but a slow CI
runner widens the window and stalls the whole test job.

RunAsync's finally now completes the queue first (so a later InvokeAsync faults
fast instead of hanging) and then drains and runs whatever remained, so every
awaiter settles. Regression test floods InvokeAsync across cancellation and
asserts all calls settle within a guard window.
marco merged commit 9a3eaa8eef into main 2026-07-19 07:03:50 +02:00
marco deleted branch fix/simulation-shutdown-hang 2026-07-19 07:03:50 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo!62
No description provided.