question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

SignalR: Client awaiting long running server method leads to timeout on server-side

See original GitHub issue

Describe the bug

I have a small setup where a C# client invokes a server method without parameters, simply waiting for an URL to return (both are v3.0.0-preview5). In my test setup, the server works on that zip file for 100s. During the call, the server prepares a zip file and returns a link to that file once finished. The server frequently sends status updates to the client.

With logs enabled on both sides and also using wireshark, I can see that the client sends pings regularly. However, if I get the source code right, the server does not process these pings due to the pending await call. The following happes:

  • after 30s, the server detects that no new message was received but continues without complains (I think this is intended)
  • after 60s, the server detects again that no frame was received and tries to abort the connection, but the invoked method continues to run. At this point, the client is not receiving status updates anymore from the server.
  • after 90s, the server detects again that no frame was received and tries to abort the connection, but the invoked method continues to run
  • after 100s, the invoked method returns, and the connection is actually aborted. OnDisconnectedAsync is called. On the client side, the error “A task was cancelled” appears.

I am not sure if this is by design (i.e. ‘don’t use async/await for very long running methods’), or if this is a bug, since the pings from the client are not processed.

If it is by design, what is the recommended alternative, when a client needs to wait for a long running methods?

To Reproduce

Hub content:

using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;

namespace WebApplication1
{
    public class Broadcaster : Hub<IBroadcaster>
    {
        public Task<string> GetUrl()
        {
            int counter = 0;

            return Task.Run(async () =>
            {
                for (int i = 0; i < 100; i++)
                {
                    await Task.Delay(TimeSpan.FromSeconds(1));
                    await this.Clients.Client(this.Context.ConnectionId).SendProgress(counter, "Progress");
                    counter += 1;
                }

                return "url";
            });
        }
    }
}

Callback interface:

using System.Threading.Tasks;

namespace WebApplication1
{
    public interface IBroadcaster
    {
        Task SendProgress(double percent, string message);
    }
}

Client:

 _connection.On("SendProgress", (double percent, string message) =>
{
    Console.WriteLine($"{percent,3:0}%: {message}\n");
});

var url = await _connection.InvokeAsync<string>("GetUrl");

Expected behavior

I would expect that during an active connection where the client sends ping messages and server sends status updates, the connection is not cancelled.

Additional context

Include the output of dotnet --info

.NET Core SDK (gemäß "global.json"):
 Version:   3.0.100-preview5-011568
 Commit:    b487ff10aa

Laufzeitumgebung:
 OS Name:     Windows
 OS Version:  10.0.17763
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\3.0.100-preview5-011568\

Host (useful for support):
  Version: 3.0.0-preview5-27626-15
  Commit:  61f30f5a23

.NET Core SDKs installed:
  2.1.602 [C:\Program Files\dotnet\sdk]
  2.2.203 [C:\Program Files\dotnet\sdk]
  3.0.100-preview5-011568 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.0-preview5-19227-01 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.0-preview5-27626-15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.0.0-preview5-27626-15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
analogrelaycommented, May 17, 2019

But more convenient would be to simply await the result 😃

Definitely agree. #5351 will make that work better since we won’t be blocking the message loop on running the Hub.

Another super hacky way you could do this is with the streaming support, since we don’t block the message loop on those requests. You can just stream only a single item back 😃. It’s a big hack though and a breaking change to your Client if you switch back and forth between streaming and non-streaming.

0reactions
Apollo3zehncommented, May 17, 2019

Thank @anurse for investigation. I switched now to a background service which handles the request to not block the hub for a long time. I no longer run into timeouts.

But more convenient would be to simply await the result 😃

Read more comments on GitHub >

github_iconTop Results From Across the Web

Understanding and Handling Connection Lifetime Events ...
This article provides an overview of the SignalR connection, reconnection, and disconnection events that you can handle, and timeout and ...
Read more >
Server-side SignalR connection fails after significant uptime
I have an application that uses a SignalR hub. A client can connect to the hub using 2 methods: Via a .NET Core...
Read more >
SignalR on the Wire – an informal description of the SignalR ...
The longPolling transport creates a long running HTTP request which the server will respond to if it has a message for the client....
Read more >
Signalr Hub Async Method
SignalR: Client awaiting long running server method leads to timeout on server-side #10257. Should asyc or non-async methods be used in SignalR Hub?...
Read more >
Advanced SignalR configuration: fine-tuning the server-side ...
When client establishes a connection with a SignalR hub, it initiates a handshake. This setting determines how long the server should wait ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found