SignalR Server side InvokeCoreAsync blocks connection when client is not processing result async
See original GitHub issueIs there an existing issue for this?
- I have searched the existing issues
Describe the bug
When calling InvokeCoreAsync
from a server side hub it waits for a result to come back from the client.
But when calling multiple InvokeCoreAsync’s to the same client, in parallel, and the client handles the On
result call synchronous, it blocks the connection to other calls to the same client.
When the client handles the On
result call async, it does not block the connection and you can call multiple InvokeCoreAsync
to the same client properly.
Expected Behavior
It should never block the connection to the client.
Steps To Reproduce
Clone https://github.com/rogerfar/signalr-concurrency-test
Run the application (both projects).
The Worker
will call all tests in parallel when the client connects.
The Worker
in the client project handles the results.
Sync client:
- Test1 runs after 1s, the client returns after 5s
- Test2 runs after 2s, the client returns after 1s
Outcome:
- Test 1 starts
- Test 1 ends
- Test 2 starts
- Test 2 ends
Expected:
- Test 1 starts
- Test 2 starts
- Test 2 ends
- Test 1 ends
Async client:
- Test3 runs after 10s, the client returns after 5s
- Test3 runs after 12s, the client returns after 1s
Outcome:
- Test 3 start
- Test 4 start
- Test 4 end
- Test 3 end
Exceptions (if any)
No response
.NET Version
7.0.100
Anything else?
I found this old issue referencing a similar issue: https://github.com/dotnet/aspnetcore/issues/10257
But worth noting is that no server/client timeouts occur during this process. Even increasing the time to 1 hour, it will properly keep the connection alive as shown in the trace log.
Issue Analytics
- State:
- Created 9 months ago
- Comments:5 (3 by maintainers)
Ok thanks, I figured as much but couldn’t find concrete evidence about it.
Thanks! It has been working very well for us, and now with the server allowing sending to the client and waiting for replies has made it even easier.
We have a product that integrates with all sorts of systems, accounting, customers homebrew, dispatching, maintenance software, basically anything that has some sort of API / SDK. Some are not cloud based like QuickBooks Desktop or Sage50, they have COM API’s or DLL’s.
We create small WPF applications or Windows services that consume these API’s (some SDK’s can’t even run as a service as they need the interactive desktop). These applications are pretty light, they setup a SignalR connection to our server and just sit there and wait, SignalR does all the heavy lifting of reconnecting, keeping alive etc.
The server is basically a command and control, and runs certain code on event loops or timers.
For example if the user submits an Invoice or Purchase Order from our product they see a spinner:
The strong point of using SignalR here is that we always have that reliable persistent connection, no need to mess around with opening firewall ports and very tight control in the sync process.
I’m wanting to write a blog post about it to show the alternative usage of SignalR but I was waiting for .NET7 to incorporate the client result sending.
We use the C# SignalR client as a proxy to various SDK’s like QuickBooksDesktop and Sage50, all those are very simple COM api’s, so no async anywhere.
The server can request data from those API’s which can result in long running tasks, which I have now rewritten to use
InvokeCoreAsync
, before we used a polling mechanism.It’s possible that a 2nd task fires while task 1 is still running to the same client, because the SDK’s can handle multiple transactions simultaneously. The issue is that task 2 is blocked until task 1 is complete, which is not desired.
For now, I have wrapped my
On
events in Tasks:Which works great.