fix(DicomReceiver): suppress late dcmrecv events after finalize#28
Conversation
Concern: suppressing
|
After the worker double-assignment race fix (0.16.0), an extreme-concurrency soak (50-parallel single-file associations) left a ~1/1500 residual: an association un-finalized from the consumer's view. Realistic batched traffic was fully clean. Root cause: dcmrecv's stdout can deliver a stray ASSOCIATION_RECEIVED for an association after the worker already finalized it and went idle. The pool bubbled it, so a consumer registered an association that never received a matching ASSOCIATION_FINALIZED — stranding it until a consumer-side reaper fired. Fix: suppress ASSOCIATION_RECEIVED when worker.finalized is set. The flag stays true between finalize and the next beginAssociation (which resets it before a genuinely new association's events arrive), so only strays are filtered. The event carries no payload, so suppression is non-destructive. Scope: FILE_RECEIVED is intentionally NOT suppressed. It represents an object already written to disk; a FILE_RECEIVED after finalize indicates finalization fired too early (a drain/stream-ordering bug), not an event to hide — dropping it would lose an image. Tracked separately for root-cause diagnosis. Adds a unit test: finalize a worker, assert a late ASSOCIATION_RECEIVED does not bubble. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0d7d79e to
9ad1b1f
Compare
|
Agreed on all points — done. I've dropped the You're right that For the file path I'll open a separate issue with instrumentation that records, for every post-finalize Branch force-pushed: |
|
Confirmed the cross-stream question is structurally closed. I captured the source stream of both line types under normal completions: Combined with the earlier worst-case run (0 post-finalize To be precise about scope: this rules out the cross-stream reorder. A same-stream chunk-timing late file (the #25 case) remains possible in theory, but it's non-destructive — your context-preservation processes it; only the guard I removed would have dropped it. So this PR stays The diagnostic lived on a separate |
MichaelLeeHobbs
left a comment
There was a problem hiding this comment.
Approved. Revision is correctly scoped: only the payload-free ASSOCIATION_RECEIVED is suppressed; FILE_RECEIVED suppression was removed (a post-finalize FILE_RECEIVED indicates premature finalization — a separate root-cause bug, not something to hide). Build/Lint/Test green; the Security Audit failure was the unrelated fast-xml-parser advisory, now fixed on main (5.8.0).
|
Merged. The deferred root cause (why late dcmrecv events arrive after finalize, and the risk that a late |
Summary
Follow-up to #27 (0.16.0). An end-to-end soak under extreme concurrency
(50 parallel single-file associations) surfaced a ~1/1500 residual: an
association left un-finalized from the consumer's view. Realistic batched
traffic was 100% clean; this only appears under pathological churn.
Root cause
dcmrecv's stdout can deliver a stray
ASSOCIATION_RECEIVED(orFILE_RECEIVED)for an association after the worker has finalized it and gone idle. The pool
bubbled the late event, so a consumer registered an association that never got
a matching
ASSOCIATION_FINALIZED— stranding it until a consumer-side reaperfired. Distinct from the #27 double-assignment race (that was the primary
cause; this is the last residual).
Fix
Suppress both events when
worker.finalizedis set. The flag stays truebetween finalize and the next
beginAssociation(which resets it before agenuinely new association's events arrive), so only strays are filtered. The
#25 late-
FILE_RECEIVED-during-finalizing path is unaffected —finalizedisset inside
finalizeAssociation, after pending pipe chunks are processed.Validation
End-to-end (50-parallel single-file, 3000 associations, 300s consumer reaper):
FILE_RECEIVED with no active associationerrorsTests
New unit test (finalize a worker, assert a late
ASSOCIATION_RECEIVEDdoesn'tbubble). Full suite: 2020/2020.
Suggested release
Patch —
0.16.1.