Search code examples
asp.net-core-signalr

SignalR Core - requests handled sequentially


If I fire many requests over one connection to one hub, they will be executed sequentially by server. So there is no advance of parallism

I have three different methods in my hub

    public Task Test1(string invokeId)
    {
        Clients.Caller.SendAsync("Hello", $"Test1: Before Delay ... InvokeID: {invokeId}");
        Task.Delay(5000).Wait();
        Clients.Caller.SendAsync("Hello", $"Test1: After Delay ...  InvokeID: {invokeId}");
        return Task.CompletedTask;
    }

    public Task Test2(string invokeId)
    {
        Clients.Caller.SendAsync("Hello", $"Test2: Before Delay ... InvokeID: {invokeId}");
        Task.Delay(2500).Wait();
        Clients.Caller.SendAsync("Hello", $"Test2: After Delay ...  InvokeID: {invokeId}");
        return Task.CompletedTask;
    }

    public Task Test3(string invokeId)
    {
        Clients.Caller.SendAsync("Hello", $"Test3: Before Delay ... InvokeID: {invokeId}");
        Task.Delay(1250).Wait();
        Clients.Caller.SendAsync("Hello", $"Test3: After Delay ...  InvokeID: {invokeId}");
        return Task.CompletedTask;
    }

I call them from client .NET application in a loop

_connection.SendAsync("Test1", "1").ContinueWith(t => Console.WriteLine($"Test1: { t.Status }"));
_connection.SendAsync("Test2", "2").ContinueWith(t => Console.WriteLine($"Test2: { t.Status }"));
_connection.SendAsync("Test3", "3").ContinueWith(t => Console.WriteLine($"Test3: { t.Status }"));

Following On method

_connection.On("Hello", new Type[] { typeof(string) }, (parameters, state) =>
{
    return Console.WriteLine((string)parameters[0]);
}, _connection);

I got following OUTPUT

  • Test1: RanToCompletion
  • Test2: RanToCompletion
  • Test3: RanToCompletion
  • Test1: Before Delay ... InvokeID: 1
  • Test1: After Delay ... InvokeID: 1
  • Test2: Before Delay ... InvokeID: 2
  • Test2: After Delay ... InvokeID: 2
  • Test3: Before Delay ... InvokeID: 3
  • Test3: After Delay ... InvokeID: 3

And if you look at that “Test2: Before Delay ... InvokeID: 2” will be called directly after “Test1: After Delay ... InvokeID: 1”. It seems that only one Hub object is allowed at same time and regardless all three calls will immediately returns “RanToCompletion” they will be executed one after another.

It seems to be very specific, but I want to build an application where each graphical WPF object will call SignalR to retrieve content. If they will be called sequentially, I have to look for another solution.

What do I miss?


Solution

  • Message will be handled sequentially by design within SignalR Core.

    I searched at sources https://github.com/aspnet/SignalR. Comment at class

    Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher
    

    function

    public override Task DispatchMessageAsync(HubConnectionContext connection, HubMessage hubMessage)
            // Messages are dispatched sequentially and will stop other messages from being processed until they complete.
            // Streaming methods will run sequentially until they start streaming, then they will fire-and-forget allowing other messages to run.
    

    So you have to create additional class which will handle your action and your Hub has to return immediately. But do not forget to pass connectionId to your class, so you can use connectionId for replies, because your original Hub is destroyed in meantime.