Repository navigation
refactor(Middleware): remove ConnectionSnapshot type - #1261
Conversation
Reviewer's guide (collapsed on small PRs)Reviewer's GuideThe middleware removes the unused ConnectionSnapshot type and updates dispatch execution to honor cancellation before callbacks, while awaiting callbacks, and after callback completion. Sequence diagram for cancellation-aware middleware dispatchsequenceDiagram
participant Caller
participant DispatchService
participant Subscription
participant CallbackTask
Caller->>DispatchService: DispatchAsync(entry, excludedConnectionId, cancellationToken)
DispatchService->>DispatchService: cancellationToken.ThrowIfCancellationRequested()
DispatchService->>Subscription: Callback(entry)
Subscription-->>DispatchService: CallbackTask
DispatchService->>CallbackTask: WaitAsync(cancellationToken)
CallbackTask-->>DispatchService: Completed
DispatchService->>DispatchService: cancellationToken.ThrowIfCancellationRequested()
DispatchService-->>Caller: Dispatch completed
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="src/middleware/BootstrapBlazor.Middleware/DefaultDispatchService.cs" line_range="63" />
<code_context>
}
- await callbackTask;
+ await callbackTask.WaitAsync(cancellationToken);
+ cancellationToken.ThrowIfCancellationRequested();
}
</code_context>
<issue_to_address>
**Callbacks outlive canceled dispatches**
When the token is canceled while a subscription callback is running, `WaitAsync` stops waiting, but `subscription.Callback(entry)` gets no token and keeps running after `DispatchAsync` returns canceled; retries run it concurrently, and later exceptions bypass subscriber-error logging, so callers see duplicate side effects and unlogged failures.
Pass cancellation into callbacks and keep their tasks observed until they finish.
</issue_to_address>
### Comment 2
<location path="src/middleware/BootstrapBlazor.Middleware/DefaultDispatchService.cs" line_range="63-64" />
<code_context>
}
- await callbackTask;
+ await callbackTask.WaitAsync(cancellationToken);
+ cancellationToken.ThrowIfCancellationRequested();
}
catch (Exception ex) when (!cancellationToken.IsCancellationRequested)
</code_context>
<issue_to_address>
**Canceled dispatch is delivered twice**
When the SignalR send completes, a local callback is still running, and cancellation leads the caller to retry, `WaitAsync` or the following cancellation check can make `DispatchAsync` return canceled after `clients.SendAsync` has already delivered the entry to the group. A caller that retries the canceled dispatch broadcasts the same entry again, so recipients process a duplicate.
Preserve an unambiguous success result once the broadcast has completed, or make retries idempotent using a dispatch identifier.
</issue_to_address>| } | ||
|
|
||
| await callbackTask; | ||
| await callbackTask.WaitAsync(cancellationToken); |
There was a problem hiding this comment.
🟡 Medium · Callbacks outlive canceled dispatches
When the token is canceled while a subscription callback is running, WaitAsync stops waiting, but subscription.Callback(entry) gets no token and keeps running after DispatchAsync returns canceled; retries run it concurrently, and later exceptions bypass subscriber-error logging, so callers see duplicate side effects and unlogged failures.
Pass cancellation into callbacks and keep their tasks observed until they finish.
Prompt for AI agents
In `src/middleware/BootstrapBlazor.Middleware/DefaultDispatchService.cs` at line 63:
**Callbacks outlive canceled dispatches**
When the token is canceled while a subscription callback is running, `WaitAsync` stops waiting, but `subscription.Callback(entry)` gets no token and keeps running after `DispatchAsync` returns canceled; retries run it concurrently, and later exceptions bypass subscriber-error logging, so callers see duplicate side effects and unlogged failures.
Pass cancellation into callbacks and keep their tasks observed until they finish.| await callbackTask.WaitAsync(cancellationToken); | ||
| cancellationToken.ThrowIfCancellationRequested(); |
There was a problem hiding this comment.
🟡 Medium · Canceled dispatch is delivered twice
When the SignalR send completes, a local callback is still running, and cancellation leads the caller to retry, WaitAsync or the following cancellation check can make DispatchAsync return canceled after clients.SendAsync has already delivered the entry to the group. A caller that retries the canceled dispatch broadcasts the same entry again, so recipients process a duplicate.
Preserve an unambiguous success result once the broadcast has completed, or make retries idempotent using a dispatch identifier.
Prompt for AI agents
In `src/middleware/BootstrapBlazor.Middleware/DefaultDispatchService.cs` at lines 63-64:
**Canceled dispatch is delivered twice**
When the SignalR send completes, a local callback is still running, and cancellation leads the caller to retry, `WaitAsync` or the following cancellation check can make `DispatchAsync` return canceled after `clients.SendAsync` has already delivered the entry to the group. A caller that retries the canceled dispatch broadcasts the same entry again, so recipients process a duplicate.
Preserve an unambiguous success result once the broadcast has completed, or make retries idempotent using a dispatch identifier.
Link issues
fixes #1260
Summary By Copilot
Regression?
Risk
Verification
Packaging changes reviewed?
☑️ Self Check before Merge
Summary by Sourcery
Remove the obsolete connection snapshot abstraction and make middleware dispatch cancellation responsive.
Bug Fixes:
Enhancements:
Build: