Conversation
In `sequential` retrieve mode every received instance went to whichever retrieve was active for the AET. When a retrieve ended early (client disconnect, timeout) the C-MOVE was not cancelled and the per-AET lock was released, so the PACS kept sending the previous study and the next retrieve for that AET received its instances. Sequential-mode subscriptions are now keyed by (AET, StudyInstanceUID), and an incoming instance is delivered only to the retrieve of its own study. Concurrent mode (matching by Move Originator Message ID) is unchanged. The per-AET semaphore becomes a per-(AET, study) one: C-MOVEs of the same study are still serialized, C-MOVEs of different studies no longer wait for each other. Instances without a StudyInstanceUID cannot be attributed in sequential mode and are dropped with the existing warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The choice of mediator topic moves into `subscription_topic()` and gets a test that routes through the mediator: if a sequential retrieve stopped subscribing to the study it requested, every sequential retrieve would 404 with all other tests green. Plus a check that concurrent retrieves still subscribe to their message ID. No behaviour change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Unfortunately, this doesn't fix the underlying problem: keying by StudyInstanceUID narrows the mixup down to the same study, but doesn't prevent it. The per-study lock is still released when the HTTP stream is dropped, not when the C-MOVE ends. If a client disconnects or times out, the peer keeps pushing the rest of the study. A second WADO-RS request for the same study then receives those leftover instances in addition to its own, so its response contains duplicates and instances from the other C-MOVE. The root cause is correlation. Without a Move Originator Message ID, a received C-STORE carries nothing that ties it to the C-MOVE that caused it. The StudyInstanceUID says which study an instance belongs to, not which request asked for it. No routing key can assign an instance to its original request reliably. What we can do instead is accept that a retrieve may receive instances from another C-MOVE, and make that harmless:
The only thing we give up is knowing which C-MOVE delivered an instance, which doesn't change what the client receives. Implemented in #73 |
|
Thanks. Agreed on all points.
A few small notes on #73, none blocking:
Drafted with an AI agent (Claude), checked against the code of both PRs. |
Fixes #71.
Problem. In
wado-rs.mode: sequential, every received instance went to whichever retrieve was active for the AET ((AET, None)). When a retrieve ended early (client disconnect, timeout), the C-MOVE kept running at the PACS while the per-AET lock was released. The next retrieve could then receive the previous study's instances.Change
StudyInstanceUID).publishdelivers by Move Originator Message ID as before; otherwise it delivers only to the retrieve of the instance's own study. The(AET, None)catch-all is gone.pool.size).StudyInstanceUIDcannot be attributed in sequential mode and are dropped with the existing warning.Tests. 7 new mediator unit tests, plus 2 on the topic a retrieve subscribes to (
subscription_topic()inwado.rs; one routes through the mediator, so it fails if a sequential retrieve stops subscribing to its requested study). The regression testan_abandoned_retrieve_does_not_leak_into_the_next_onefails onmain(the same scenario against main's API) and passes here.cargo testpasses on stable and on MSRV 1.91; the existing Orthanc tests are STOW-only, so they show no regression elsewhere rather than covering this path.cargo fmt --checkis clean, and clippy reports the same warnings asmain.Known limitation, pre-existing and not changed here.
publishholds the callbacks lock while it sends into a retrieve's 1-slot channel, so one slow client can delay delivery to the others until it reads or disconnects.Not in this PR (happy to follow up):
Drafted with an AI agent (Claude), checked against the source and tested as above.