SignalR connection timeouts if browser tab is inactive

Viewed 4219

If I keep browser tab active (open it at least once per 5-6 mins) my WebSocket connection keeps alive through ping requests (see attached screenshot). But if I abandon the tab for 10 minutes or so, /ping requests stop happening and WebSocket connection terminates. Any explanation for this and how can it be bypassed to keep connection alive while device is awake?

PS: I suspect that our recent migration to Azure Web services could be related. Or some browser policies could change. Having SignalR implemented for a few years we now experience such issues for the first time.

enter image description here

4 Answers

We were able to mitigate the issue by increasing disconnect timeout to 90s and setting keep alive property to 10s on the server (ASP.NET app).

GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(90);
GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);

But I'm still waiting for an official response on Github.

It could be a tab freezing feature on the browser.

I have copied this directly from Microsoft documentation

Some browsers have a tab freezing or sleeping feature to reduce computer resource usage for inactive tabs. This can cause SignalR connections to close and may result in an unwanted user experience. Browsers use heuristics to figure out if a tab should be put to sleep, such as:

  • Playing audio
  • Holding a web lock
  • Holding an IndexedDB lock
  • Being connected to a USB device
  • Capturing video or audio
  • Being mirrored
  • Capturing a window or display

Note: These heuristics may change over time or differ between browsers. Check your support matrix and figure out what method works best for your scenarios.

To avoid putting an app to sleep, the app should trigger one of the heuristics that the browser uses.

The following code example shows how to use a Web Lock to keep a tab awake and avoid an unexpected connection closure.

JavaScript

Copy
var lockResolver;
if (navigator && navigator.locks && navigator.locks.request) {
    const promise = new Promise((res) => {
        lockResolver = res;
    });

    navigator.locks.request('unique_lock_name', { mode: "shared" }, () => {
        return promise;
    });
}

For the preceding code example:

Web Locks are experimental. The conditional check confirms that the browser supports Web Locks. The promise resolver (lockResolver) is stored so that the lock can be released when it's acceptable for the tab to sleep. When closing the connection, the lock is released by calling lockResolver(). When the lock is released, the tab is allowed to sleep.

This issue has fixed already. https://github.com/dotnet/aspnetcore/issues/31079

Solution is just to update your signalr npm package to the latest version. https://www.npmjs.com/package/@microsoft/signalr

Since Chrome 88, intensive throttling happens to timers in the following conditions.(https://developer.chrome.com/blog/timer-throttling-in-chrome-88/)

  • The page has been hidden for more than 5 minutes.
  • The chain count is 5 or greater.
  • The page has been silent for at least 30 seconds.
  • WebRTC is not in use.

Above timer throttling causes signalr connection broken.

Related