Search code examples
c#.net-coresignalr

SignalR reconnection with custom RetryPolicy does not respect that policy and adds additional delays


I'm using the SignalR .NET client and server on .NET 6, and the reconnection delay does seem to work differently than I'd expect. I'm using the following code on the server to define a custom RetryPolicy:

connection = new HubConnectionBuilder()
  .WithUrl(url)
  .WithAutomaticReconnect(new RetryPolicy())
  .Build();

public class RetryPolicy : IRetryPolicy
{
  public TimeSpan? NextRetryDelay(RetryContext retryContext)
  {

    var delay = retryContext.PreviousRetryCount switch
    {
      < 5 => TimeSpan.FromSeconds(0),
      < 10 => TimeSpan.FromSeconds(5),
      _ => TimeSpan.FromSeconds(10),
    };
    Console.WriteLine($"NextRetryDelay count: {retryContext.PreviousRetryCount} elapsed: {retryContext.ElapsedTime} delay: {delay}");
    return delay;
  }
}

When I trigger a disconnect I get the following output from this:

NextRetryDelay count: 0 elapsed: 00:00:00 delay: 00:00:00
NextRetryDelay count: 1 elapsed: 00:00:08.1187903 delay: 00:00:00
NextRetryDelay count: 2 elapsed: 00:00:12.1619132 delay: 00:00:00
NextRetryDelay count: 3 elapsed: 00:00:16.2117377 delay: 00:00:00
NextRetryDelay count: 4 elapsed: 00:00:20.2883145 delay: 00:00:00
NextRetryDelay count: 5 elapsed: 00:00:24.3593463 delay: 00:00:05
NextRetryDelay count: 6 elapsed: 00:00:33.4387382 delay: 00:00:05
NextRetryDelay count: 7 elapsed: 00:00:42.4919265 delay: 00:00:05
NextRetryDelay count: 8 elapsed: 00:00:51.5778513 delay: 00:00:05
NextRetryDelay count: 9 elapsed: 00:01:00.6560698 delay: 00:00:05
NextRetryDelay count: 10 elapsed: 00:01:09.7329655 delay: 00:00:10
NextRetryDelay count: 11 elapsed: 00:01:23.7992479 delay: 00:00:10
NextRetryDelay count: 12 elapsed: 00:01:37.8539388 delay: 00:00:10

For every delay there seem to be roughly 4 seconds of an additional delay beyond the time I specific in this custom RetryPolicy. The second delay even has an additional 8 seconds, and these values seem pretty reproducible, though not perfectly so.

I adjusted the actual values for testing here, but my goal is to have a few very quick retries at the start. This does not seem possible with this ~8 second initial delay that comes out of nowhere.

Where do these additional delays come from, and how do I get .NET to respect the RetryPolicy I set here?


Solution

  • When you implement a custom IRetryPolicy, you have to take into account that the delay you put is added to the time the HubConnectionBuilder takes before trying to reconnect.

    So, assuming your SignalR client is retrying for 4 seconds before returning that the reconnection was not possible, and adding the delay you set to that, you get the elapsed time you're getting.

    4 seconds spent trying to reconnect + 5 seconds delay = 9 seconds added to the previous elapsed time.

    Knowing that you can change your delay to fewer (or none) seconds to make the wait time faster.

    I tried this behavior on a client that takes roughly 21 seconds to reconnect and added a 5 second delay, and got a steady 26 second wait time, so the RetryPolicy is working just fine.

    NextRetryDelay count: 0 elapsed: 00:00:00 delay: 5
    NextRetryDelay count: 1 elapsed: 00:00:26.1568207 delay: 5
    NextRetryDelay count: 2 elapsed: 00:00:52.2934825 delay: 5
    NextRetryDelay count: 3 elapsed: 00:01:18.3989711 delay: 5
    NextRetryDelay count: 4 elapsed: 00:01:44.5205752 delay: 5
    NextRetryDelay count: 5 elapsed: 00:02:10.6291636 delay: 5
    NextRetryDelay count: 6 elapsed: 00:02:36.7462710 delay: 5
    NextRetryDelay count: 7 elapsed: 00:03:02.8838054 delay: 5
    NextRetryDelay count: 8 elapsed: 00:03:29.0223075 delay: 5
    NextRetryDelay count: 9 elapsed: 00:03:55.1337186 delay: 5
    NextRetryDelay count: 10 elapsed: 00:04:21.2564770 delay: 5