Skip to content

Send Keep-Alive Ping Immediately When Previous Ping Is Overdue #63195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

ReaganYuan
Copy link

@ReaganYuan ReaganYuan commented Aug 8, 2025

Send Keep-Alive Ping Immediately When Previous Ping Is Overdue

Trigger Client-to-Server Keep-Alive ping immediately if nextPing is overdue. The behavior should remain in parity as sending it immediately vs adding it to setTimeout with 0 delay are the same.

Description

We've observed that some browser clients occasionally enter a hidden (but not frozen) state, during which JavaScript timers are paused. As a result, the Client-to-Server Keep-Alive ping fails to fire, causing the server to reach the ClientTimeoutInterval and disconnect the client.
The client then attempts to reconnect, but because the browser remains in a hidden state, setTimeout continues to be paused, preventing the ping from firing again. This leads to repeated disconnections and reconnections until the browser either becomes frozen or returns to an active state. This behavior results in unnecessary reconnect traffic on the server.

Note: The frozen state works as expected, where Server will disconnect Client once the ClientTimeoutInterval has been exceeded. And the Client will not reconnect until the browser is unfrozen.

This issue doesn't occur consistently across all hidden browser states. We suspect it may be influenced by the following,

  • Browser type
  • Power-saving mode
  • Desktop vs. laptop environments

We've patched our SignalR client (v8.0.7) to immediately fire the Keep-Alive ping if nextPing is in the past, rather than relying on setTimeout, which may be paused. This change helped resolve the repeated reconnections for such sessions.
{Detail}

@github-actions github-actions bot added the area-signalr Includes: SignalR clients and servers label Aug 8, 2025
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Aug 8, 2025
Copy link
Contributor

Thanks for your PR, @@ReaganYuan. Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@@ -726,7 +726,12 @@ export class HubConnection {
{
let nextPing = this._nextKeepAlive - new Date().getTime();
if (nextPing < 0) {
nextPing = 0;
if (this._connectionState === HubConnectionState.Connected) {
this._sendMessage(this._cachedPingMessage).catch((e) => {
Copy link
Member

Choose a reason for hiding this comment

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

This change is different from what you showed me. The check is now inside the if (this._pingServerHandle === undefined) condition, but isn't the problem that we set a timer but it isn't being fired? So this new code will never be hit in the problematic case?

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 community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants