Aspnetcore: SignalR message order?

Created on 10 Apr 2019  路  7Comments  路  Source: dotnet/aspnetcore

Hi.

I have a question about signalr order or messages that are received by client.
Situation (server code):

...
await hubContext.Clients.All.SendAsync("EventA", "123");
await hubContext.Clients.All.SendAsync("EventB", "456");
...

Can it happen that some client will receive message 456 before message 123 (note that method name is different)?

area-signalr

Most helpful comment

Why would Thread.Sleep change anything? Feels unreliable.

That is indeed unreliable. Also Thread.Sleep is dangerous when used in an async context since it blocks the entire thread which can lead to thread-pool starvation. Use await Task.Delay(1) when in an async context. Anything that "blocks" without using await in an async context can cause significant performance problems.

If ordering is not guaranteed what the purpose of SendAsync api that allows you to await something?

You are awaiting successful entry of the message in to the queue of messages to be sent out to the client. If you have no interest in awaiting that, you don't have to.

I also want to clarify something about our ordering. We do not enforce ordering at a protocol level, thus cannot guarantee that ordering is preserved in all cases. Nor does SignalR provide any facility (built-in) to ensure ordering is preserved. However, in practice most messages will be consistently ordered across multiple clients/servers.

When you have a single server (and all clients are connected to that server), ordering should be preserved, since SendAsync completes when the message enters the in-memory queues for each client, and the next SendAsync will thus insert the message after the previous one.

When multiple servers are involved, connected via a backplane such as Redis or the Azure SignalR Service, it depends on how the backplane behaves. If two servers send messages to all clients (messages A and B), each server will send that message to the backplane to be distributed to all servers in the cluster. As long as the backplane preserves ordering, they should remain ordered. However, if the backplane doesn't preserve ordering, some clients may see A B and other may see B A.

The official backplanes (Redis and the Azure SignalR Service) should preserve ordering, since the underlying technologies used are ordered.

However, as I said above, since SignalR doesn't enforce this ordering itself, we avoid making any guarantees as to ordering.

All 7 comments

Yes, ordering is not guaranteed between messages. If order is important, I'd suggest you add sequencing information to the messages themselves.

await hubContext.Clients.All.SendAsync("EventA", DateTime.UtcNow.Ticks, "123");
Thread.Sleep(1);
await hubContext.Clients.All.SendAsync("EventB", DateTime.UtcNow.Ticks, "456");

or use messaging queue

Why would Thread.Sleep change anything? Feels unreliable.
Is ordering guaranteed if action name is same?
If ordering is not guaranteed what the purpose of SendAsync api that allows you to await something? What are we awaiting then?

  • messaging delivery order is not guaranteed. but you can order them via thread.sleep. it's not guaranteed too. :)
  • best practice is use your own client-side order in your javascript code.
await hubContext.Clients.All.SendAsync("EventA", DateTime.UtcNow.Ticks, "123");
Thread.Sleep(1);
await hubContext.Clients.All.SendAsync("EventB", DateTime.UtcNow.Ticks, "456");

DateTime.UtcNow.Ticks will produce almost unique int.
check that value in your javascript code. order them in a queue object (as stack) and get them one by one by the Ticks value.

By the way, you might be looking for a faulty architecture. In many scenarios, you shouldn't need that.

Why would Thread.Sleep change anything? Feels unreliable.

That is indeed unreliable. Also Thread.Sleep is dangerous when used in an async context since it blocks the entire thread which can lead to thread-pool starvation. Use await Task.Delay(1) when in an async context. Anything that "blocks" without using await in an async context can cause significant performance problems.

If ordering is not guaranteed what the purpose of SendAsync api that allows you to await something?

You are awaiting successful entry of the message in to the queue of messages to be sent out to the client. If you have no interest in awaiting that, you don't have to.

I also want to clarify something about our ordering. We do not enforce ordering at a protocol level, thus cannot guarantee that ordering is preserved in all cases. Nor does SignalR provide any facility (built-in) to ensure ordering is preserved. However, in practice most messages will be consistently ordered across multiple clients/servers.

When you have a single server (and all clients are connected to that server), ordering should be preserved, since SendAsync completes when the message enters the in-memory queues for each client, and the next SendAsync will thus insert the message after the previous one.

When multiple servers are involved, connected via a backplane such as Redis or the Azure SignalR Service, it depends on how the backplane behaves. If two servers send messages to all clients (messages A and B), each server will send that message to the backplane to be distributed to all servers in the cluster. As long as the backplane preserves ordering, they should remain ordered. However, if the backplane doesn't preserve ordering, some clients may see A B and other may see B A.

The official backplanes (Redis and the Azure SignalR Service) should preserve ordering, since the underlying technologies used are ordered.

However, as I said above, since SignalR doesn't enforce this ordering itself, we avoid making any guarantees as to ordering.

So, overall, SignalR guarantees message ordering unless you are using a backplane, in which case the order is only guaranteed if the backplane guarantees the order, which is true of the officially supported backplanes. So in practice, ordering is guaranteed and it would be a waste of time to build my own message ordering system.

Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue.

This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue!

Was this page helpful?
0 / 5 - 0 ratings