SignalR Core: Heartbeat and Redis

public async Task OnConnectedAsync()
{
...
_clientList.CreateUser(Context.ConnectionId);
...
}
public async Task OnDisconnectedAsync(Exception ex)
{
...
_clientList.RemoveUser(Context.ConnectionId);
...
}

Scenario 1

Redis has crashed and a new connection has establisted. _clientList.CreateUser method will also crash. And, we can’t see the new client on the Redis.

Scenario 2

Redis was working without problem and then it crashed or some network problem have occurred between server and redis. So, what will happen this in case ?

Solutions

Scenario 1 has a basic solution. You can ignore the new connections while Redis is down. Which means, you don’t allow new connections. In order to accomplish this, basically call the Context.Abort()

public async Task OnConnectedAsync()
{
try {
...
_clientList.CreateUser(Context.ConnectionId);
...
}
catch (Exception ex) {
// put your logger logic here
Context.Abort();
}
}
private void Heartbeat()
{
var heartbeat = Context.Features.Get<IConnectionHeartbeatFeature>();
heartbeat.OnHeartbeat(state => {
(HttpContext context ,string connectionId) = ((HttpContext, string))state;
var ClientList = context.RequestServices.GetService<IClientList>();
ClientList.LatestPing(connectionId);
}, (Context.GetHttpContext(), Context.ConnectionId));
}
Result

Conclusion

To sum up, we can use the latest ping time in order to deal with many cases. Of course, SignalR Core itself needs more features for edge cases. On the other hand, this solution is not perfect. Depending on the system design, a completely different service approach might be required.

UPDATE

SignalR Core 3.0’s heartbeat implementation has default interval as 1 second. If you think it is too high frequency. You can slow down it with Random. Below code will work randomly 10 seconds.

Random rnd = new Random((int)DateTime.Now.Ticks);
public async Task OnConnectedAsync()
{
var heartbeat = Context.Features.Get<IConnectionHeartbeatFeature>();heartbeat.OnHeartbeat(state => {
if (rnd.Next(0, 100) >= 10)
return;
(HttpContext context ,string connectionId) = ((HttpContext, string))state;
var ClientList = context.RequestServices.GetService<IClientList>();
ClientList.LatestPing(connectionId);
}, (Context.GetHttpContext(), Context.ConnectionId));
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store