Skip to content

[SignalR] Extra backpressure timeout#66318

Merged
wtgodbe merged 1 commit intomainfrom
wtgodbe/PortIt414
Apr 14, 2026
Merged

[SignalR] Extra backpressure timeout#66318
wtgodbe merged 1 commit intomainfrom
wtgodbe/PortIt414

Conversation

@wtgodbe
Copy link
Copy Markdown
Member

@wtgodbe wtgodbe commented Apr 14, 2026

The changes enhance SignalR's message buffering by adding a timeout mechanism for backpressure situations and expanding test coverage to verify the new behavior.

  • src/SignalR/common/Shared/MessageBuffer.cs: Updated backpressure logic to use a 5-second cancellation token with TimeProvider and improved timer initialization.
  • src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests/Internal/MessageBufferTests.cs: Added new tests for default and cancellable backpressure writes and renamed an existing test for clarity.
  • src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs: Introduced and adjusted scenarios to test backpressure timeout handling in hub connection scenarios.

#### AI description  (iteration 1)
#### PR Classification
This pull request fixes backpressure cancellation issues in SignalR by introducing an extra 5-second timeout.

#### PR Summary
The changes enhance SignalR's message buffering by adding a timeout mechanism for backpressure situations and expanding test coverage to verify the new behavior.
- `src/SignalR/common/Shared/MessageBuffer.cs`: Updated backpressure logic to use a 5-second cancellation token with TimeProvider and improved timer initialization.
- `src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests/Internal/MessageBufferTests.cs`: Added new tests for default and cancellable backpressure writes and renamed an existing test for clarity.
- `src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs`: Introduced and adjusted scenarios to test backpressure timeout handling in hub connection scenarios.
<!-- GitOpsUserAgent=GitOps.Apps.Server.pullrequestcopilot -->
Copilot AI review requested due to automatic review settings April 14, 2026 19:39
@github-actions github-actions Bot added the area-signalr Includes: SignalR clients and servers label Apr 14, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a bounded timeout for SignalR MessageBuffer backpressure waits (to avoid indefinite blocking) and expands/adjusts server tests to validate the new timeout-driven cancellation behavior in both unit-level MessageBuffer tests and higher-level HubConnectionHandler reconnect scenarios.

Changes:

  • Add a 5-second cancellation timeout to MessageBuffer’s ack-based backpressure wait, using TimeProvider when available.
  • Extend MessageBufferTests with new coverage for default timeout cancellation vs. caller-provided cancellation, and rename the existing pipe-backpressure cancellation test for clarity.
  • Extend HubConnectionHandlerTests to validate a “backpressure timeout” close scenario alongside existing abort/ping-timeout scenarios.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/SignalR/common/Shared/MessageBuffer.cs Adds a 5-second timeout to buffered backpressure waits and wires in TimeProvider for timer/CTS behavior.
src/SignalR/server/Core/src/HubConnectionContext.cs Adjusts CloseMessage write cancellation behavior when using stateful reconnect/message buffering.
src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests/Internal/MessageBufferTests.cs Adds tests for buffered backpressure timeout and cancellation; renames and tweaks an existing cancellation test.
src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs Adds a new close scenario to validate cancellation via backpressure timeout in reconnect + buffering flows.
Comments suppressed due to low confidence (1)

src/SignalR/server/Core/src/HubConnectionContext.cs:290

  • Removing the 5-second short-lived cancellation token for CloseMessage writes means SendCloseAsync (which calls WriteAsync(closeMessage, ignoreAbort: true) with the default CancellationToken.None) can now block indefinitely under pipe backpressure when the connection is still open but the client isn't reading. The new MessageBuffer backpressure timeout only applies when the message buffer is over its limit; it doesn't bound the PipeWriter.WriteAsync/Flush path for CloseMessage in the non-buffer-full case. Consider restoring a bounded timeout for CloseMessage writes here (e.g., a 5s CTS using the hubConnectionContext TimeProvider) while still not linking to ConnectionAborted so the CloseMessage can be attempted even if abort has already been requested.
                    var connectionToken = hubConnectionContext.ConnectionAborted;
                    if (message is CloseMessage)
                    {
                        // If it's a CloseMessage, we might already have triggered the ConnectionAborted token
                        // We would like to successfully send the CloseMessage for graceful close which means we can't use the ConnectionAborted token.
                        connectionToken = CancellationToken.None;
                    }

                    // MessageBuffer can wait on things other than the PipeWriter (which is canceled by other means)
                    // So we need to make sure the cancellation token passed to it is also canceled when the connection is aborted
                    using var _ = CancellationTokenUtils.CreateLinkedToken(connectionToken, cancellationToken, out var linkedToken);
                    var result = await messageBuffer.WriteAsync(message, linkedToken);

Comment on lines +625 to +627
// Write will hit pipe backpressure
var writeTask = messageBuffer.WriteAsync(new SerializedHubMessage(new InvocationMessage("t", new object[] { new byte[40] })), cts.Token);
Assert.False(writeTask.IsCompleted);
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

In these new buffered-backpressure tests, the write is blocked waiting for Ack (MessageBuffer backpressure), not primarily by the PipeWriter pause/resume thresholds. The comment currently says "pipe backpressure", which is misleading for what the test is asserting. Consider updating the comment to reflect that this is MessageBuffer/ack-based backpressure.

Copilot uses AI. Check for mistakes.
#if NET8_0_OR_GREATER
timeProvider ??= TimeProvider.System;
_timer = new(AckRate, timeProvider);
_timeProvider = timeProvider;
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

MessageBuffer now stores the provided TimeProvider without validating it (previously it fell back to TimeProvider.System when null). Since this shared source is compiled into multiple assemblies, a null TimeProvider passed accidentally would become a NullReferenceException later (timer creation / backpressure CTS). Consider either throwing ArgumentNullException when timeProvider is null or restoring the previous timeProvider ??= TimeProvider.System fallback before assigning/using it.

Suggested change
_timeProvider = timeProvider;
_timeProvider = timeProvider ?? TimeProvider.System;

Copilot uses AI. Check for mistakes.
@wtgodbe wtgodbe enabled auto-merge (squash) April 14, 2026 20:47
@wtgodbe wtgodbe merged commit 5e9f326 into main Apr 14, 2026
31 checks passed
@wtgodbe wtgodbe deleted the wtgodbe/PortIt414 branch April 14, 2026 22:22
@dotnet-policy-service dotnet-policy-service Bot added this to the 11.0-preview4 milestone Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-signalr Includes: SignalR clients and servers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants