Skip to content

Conversation

@Kyemate
Copy link
Contributor

@Kyemate Kyemate commented Oct 30, 2025

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.

@coderabbitai
Copy link

coderabbitai bot commented Oct 30, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in 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 conversion
src/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 consumption
src/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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Method signature changes: GetUserSessionLogs converted from Task<T[]> return to IAsyncEnumerable<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 foreach properly 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, 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 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 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between fdf2eb6 and 7035364.

📒 Files selected for processing (2)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Management/UsersPage.razor.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/SignalR/AppHub.cs (2 hunks)
🔇 Additional comments (4)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/SignalR/AppHub.cs (3)

2-2: LGTM!

The System.Runtime.CompilerServices namespace is required for the [EnumeratorCancellation] attribute used in the method signature.


70-73: LGTM!

The method signature correctly implements the streaming pattern with IAsyncEnumerable<DiagnosticLogDto> and proper cancellation support via [EnumeratorCancellation].


75-81: LGTM!

The cancellation token is correctly passed to the database query, and yield break is the proper way to exit early from an async enumerable method.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Management/UsersPage.razor.cs (1)

196-204: LGTM! Client-side streaming implementation is correct.

The client properly consumes the streaming API using await foreach with StreamAsync<DiagnosticLogDto>. Logs are processed incrementally with proper cancellation support, and the diagnostic modal is displayed after streaming completes.

Note: While this implementation is correct, the streaming won't prevent the original SignalR message size error due to the server-side issue where logs are still fetched all at once from the target client (see comments on AppHub.cs).

Comment on lines 83 to 94
var logs = await Clients.Client(userSessionSignalRConnectionId)
.InvokeAsync<DiagnosticLogDto[]>(SignalRMethods.UPLOAD_DIAGNOSTIC_LOGGER_STORE, cancellationToken);

if (logs is null || logs.Length is 0)
yield break;

foreach (var log in logs)
{
cancellationToken.ThrowIfCancellationRequested();
yield return log;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

This implementation doesn't solve the root cause of the SignalR message size error.

The PR aims to fix the "maximum message size of 32768B was exceeded" error when reading user session logs. However, this implementation only streams logs from the hub to the admin client, but the error occurs when the target user's client returns all logs at once to the hub via InvokeAsync<DiagnosticLogDto[]>.

Architecture flow:

  1. Admin client → GetUserSessionLogs → Hub
  2. Hub → InvokeAsync<DiagnosticLogDto[]> → Target client ⚠️ 32KB error occurs here
  3. Target client → returns all logs at once → Hub
  4. Hub → yields logs one by one → Admin client ✅ (streaming works here, but too late)

Impact: Users will still encounter the same SignalR message size error for sessions with large log volumes.

To properly fix this issue, the target client (the one whose logs are being read) must also stream its logs or batch them into smaller chunks. Consider:

  • Implementing a streaming method on the client side that yields logs incrementally
  • Having the client batch logs into smaller arrays (e.g., 100 logs per batch)
  • Implementing a different retrieval mechanism that doesn't rely on a single InvokeAsync call with a potentially large payload

@ysmoradi ysmoradi marked this pull request as draft November 4, 2025 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reading another users log through User management causes SignalR message size error.

1 participant