Made ReadUserSessionLogs stream logs with IAsyncEnumerable
closes #11480
Summary by CodeRabbit
-
Improvements
- Diagnostic logs now stream incrementally, providing faster initial response times when retrieving user session logs.
- Added cancellation support for log retrieval operations, enabling users to stop ongoing requests.
[!IMPORTANT]
Review skipped
Auto incremental reviews are disabled on this repository.
Please check the settings in the CodeRabbit UI or the
.coderabbit.yamlfile in this repository. To trigger a single review, invoke the@coderabbitai reviewcommand.You can disable this status message by setting the
reviews.review_statustofalsein the CodeRabbit configuration file.
Walkthrough
The user session logs retrieval is converted from batch-based RPC to SignalR streaming. The server method now uses IAsyncEnumerable<DiagnosticLogDto> to yield logs incrementally rather than returning all logs at once. The client consumes logs via StreamAsync and processes them as they arrive, with full cancellation support.
Changes
| Cohort / File(s) | Summary |
|---|---|
Server streaming conversionsrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/SignalR/AppHub.cs |
Method GetUserSessionLogs signature changed from Task<DiagnosticLogDto[]> to IAsyncEnumerable<DiagnosticLogDto>. Added [EnumeratorCancellation] CancellationToken parameter. Implemented streaming enumeration with per-log cancellation checks. Changed error handling to yield break when connection ID not found. Added System.Runtime.CompilerServices directive. |
Client streaming consumptionsrc/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Management/UsersPage.razor.cs |
Replaced direct RPC call with hubConnection.StreamAsync<DiagnosticLogDto>("GetUserSessionLogs", ...) using await foreach loop. Each received log is enqueued into DiagnosticLogger.Store incrementally. Cancellation token now properly respected throughout stream lifetime. |
Sequence Diagram(s)
sequenceDiagram
participant Client as Client<br/>(UsersPage)
participant Hub as Server<br/>(AppHub)
participant DB as Database
rect rgb(230, 245, 240)
Note over Client,DB: New Streaming Approach
Client->>Hub: StreamAsync("GetUserSessionLogs", id, token)
activate Hub
Hub->>DB: Query connection ID (with token)
DB-->>Hub: Connection ID
loop For Each Log
Hub->>Hub: Fetch next log via InvokeAsync
Hub->>Hub: Check cancellation
Hub-->>Client: yield DiagnosticLogDto
activate Client
Client->>Client: Enqueue to DiagnosticLogger.Store
deactivate Client
end
Hub-->>Client: Stream complete
deactivate Hub
Client->>Client: Publish SHOW_DIAGNOSTIC_MODAL
end
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
-
Method signature changes:
GetUserSessionLogsconverted fromTask<T[]>return toIAsyncEnumerable<T>, requiring verification that all callers handle streaming correctly -
Cancellation token handling: Review
[EnumeratorCancellation]attribute usage and per-log cancellation checks to ensure proper propagation -
Error handling pattern shift: Verify that
yield break(instead of empty array return) is handled correctly by all consumers -
Stream lifecycle: Confirm that the client-side
await foreachproperly completes and triggers modal publication
Poem
🐰 Logs now flow like streams so wide,
No more bursting at the seide!
Each message drifts down, bit by bit,
The socket smiles—what perfect fit!
SignalR's heart skips with delight,
As batches transform to flowing light. 🌊
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title Check | ✅ Passed | The PR title "Made ReadUserSessionLogs stream logs with IAsyncEnumerable" directly captures the primary change in the changeset. The code modifications convert GetUserSessionLogs from a synchronous-style return of all logs (Task<DiagnosticLogDto[]>) to a streaming approach using IAsyncEnumerable<DiagnosticLogDto>, which is exactly what the title communicates. While there is a minor semantic difference between "Read" in the title and "Get" in the actual method name, this does not undermine the title's accuracy since both terms semantically relate to the functionality of retrieving user session logs. The title is concise, clear, and specific enough for a teammate to understand the core change. |
| Linked Issues Check | ✅ Passed | The linked issue #11480 describes a SignalR message size error ("The maximum message size of 32768B was exceeded") that occurs when reading another user's log through user management. The root cause is that all logs are returned in a single message, exceeding the size limit. The PR directly addresses this by converting GetUserSessionLogs from returning a Task<DiagnosticLogDto[]> (all logs at once) to an IAsyncEnumerable<DiagnosticLogDto> that streams logs individually to the client via hubConnection.StreamAsync(). This architectural change ensures that logs are transmitted incrementally rather than in a single large message, effectively bypassing the message size constraint. The addition of cancellation token support is also a beneficial enhancement that aligns with best practices for async streaming operations. |
| Out of Scope Changes Check | ✅ Passed | All code changes in the pull request are directly related to implementing the streaming solution for issue #11480. The modifications in UsersPage.razor.cs replace the direct RPC call with a streaming pattern using hubConnection.StreamAsync(), while the changes in AppHub.cs convert the GetUserSessionLogs method to return IAsyncEnumerable<DiagnosticLogDto> with streaming implementation. The addition of the System.Runtime.CompilerServices using directive is necessary to support the [EnumeratorCancellation] attribute required for proper cancellation handling in async enumerable methods. No extraneous changes, refactoring, or unrelated modifications are present in the changeset. |
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.