Skip to content Skip to sidebar Skip to footer

Signalr And/or Timer Issues Since Chrome 88

We have an ASP.Net WebForms application that uses SignalR (v2.4.1) to do some bi-directional communications between server and client. It's worked fine for years: connections are s

Solution 1:

We're as well facing issues with our signalR (WebSockets as transport). We're not able to reproduce it in our lab. The HAR files of our customer and extended logging provided us only the information that the client "consuming only after following interesting groups" is not sending pings within the default 30 seconds needed to keep the connection. Therefore the server closes the connection. We added logs in the signalR client library and only saw the ping timer not being hit on time. No error, no nothing. (Client is JavaScript and the issue occurred on customer site in chrome 87 (throttling was implemented there already for half of the chrome users - https://support.google.com/chrome/a/answer/7679408#87))

And the world is slowly getting aware of "an issue": https://github.com/SignalR/SignalR/issues/4536

Our quick help for our customers will be to create an ET with a manual broadcast ping-pong mechanism from the server site and each client will have to answer. Avoiding being dependent on the JavaScript ping in the signalR library until a "better" solution or fix is provided.

Solution 2:

Microsoft have released SignalR 2.4.2, which should address the issue natively and avoid the need for any manual workarounds.

Nuget package available here, and the list of fixed issues is here

Solution 3:

As a workaround, javascript library that does the ping can be modified, to slightly change the way that it uses the timers. One of the conditions for intensive throttling is that the setTimeout()/setInterval() chain count is 5+. This can be avoided for recurring calls, by using a web worker. The main thread can post a dummy message to the web worker, which does nothing other than posting a dummy message back to the main thread. The subsequent setTimeout() call can be made on the message event from the web worker.

i.e.,

  1. main_thread_ping_function :- doPing() -> post_CallMeBack_ToWebWorker()
  2. web_worker :- onmessage -> post_CallingYouBack_ToMainThread()
  3. main_thread :- web_worker.onmessage -> setTimeout(main_thread_ping_function, timeoutValue)

Since the setTimeout() is called on a message from web worker, rather than from the setTimout() execution flow, the chain length remains one, and thus no intensive throttling would be done by chrome 88+.

Note that, chained setTimeout() calls in a web worker are not throttled by chrome at the moment, and thus defining the timer functionality inside a web worker, and acting on the messages(to perform ping) from web worker, too solves the problem. However, if chrome developers decide to throttle the timers in web workers too, in the future, it gets broken again.

A utility(similar to java scheduled executor) which allows scheduling of callbacks using web workers, to avoid throttling, by context switching:

classNonThrottledScheduledExecutor {


  constructor(callbackFn, initialDelay, delay) {
    this.running = false;
    this.callback = callbackFn;
    this.initialDelay = initialDelay;
    this.delay = delay;
  };

  start() {
    if (this.running) {
      return;
    }
    this.running = true;
    // Code in worker.let workerFunction = "onmessage = function(e) { postMessage('fireTimer'); }";
    this.worker = newWorker(URL.createObjectURL(newBlob([workerFunction], {
      type: 'text/javascript'
    })));
    // On a message from worker, schedule the next round.this.worker.onmessage = (e) =>setTimeout(this.fireTimerNow.bind(this), this.delay);
    // Start the first round.setTimeout(this.fireTimerNow.bind(this), this.initialDelay);

  };

  fireTimerNow() {
    if (this.running) {
      this.callback();
      // dummy message to be posted to web worker.this.worker.postMessage('callBackNow');
    }
  };

  stop() {
    if (this.running) {
      this.running = false;
      this.worker.terminate();
      this.worker = undefined;
    }
  };

};
<buttononclick="startExecutor()">Start Executor</button><buttononclick="stopExecutor()">Stop Executor</button><divid="op"></div><script>var executor;
  
  functionstartExecutor() {
    if (typeof(executor) == 'undefined') {
      // Schedules execution of 'doThis' function every 2seconds, after an intial delay of 1 sec
      executor = newNonThrottledScheduledExecutor(doThis, 1000, 2000);
      executor.start();
      console.log("Started scheduled executor");
    }
  }

  functionstopExecutor() {
    if (typeof(executor) != 'undefined') {
      executor.stop();
      executor = undefined;
      document.getElementById("op").innerHTML = "Executor stopped at " + l;
    }
  }

  var l = 0;

  functiondoThis() {
    l = l + 1;
    document.getElementById("op").innerHTML = "Executor running... I will run even when the my window is hidden.. counter: " + l;
  }
</script>

Solution 4:

I know that it does not solve the problem altogether with chrome, however, the new edge that uses chromium engine has added a few new settings to govern the timeouts (since it was affected too by the change). There is a new whitelisting option that gives at least the power to the users to decide which pages are excluded from this behavior. I honestly do believe that these setting will be added by google sooner or later. Until then we recommend our customers to switch to edge if they are affected.

You can find it in settings\system: screenshot

Post a Comment for "Signalr And/or Timer Issues Since Chrome 88"