Skip to content

Canceled client result can result in connection being closed #43054

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
BrennanConroy opened this issue Aug 2, 2022 · 4 comments · Fixed by #43249
Closed

Canceled client result can result in connection being closed #43054

BrennanConroy opened this issue Aug 2, 2022 · 4 comments · Fixed by #43249
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-signalr Includes: SignalR clients and servers bug This issue describes a behavior which is not expected - a bug. Priority:1 Work that is critical for the release, but we could probably ship without
Milestone

Comments

@BrennanConroy
Copy link
Member

Using the new client results feature in SignalR, if you pass in a cancellation token to InvokeAsync and cancel it, the server-side will properly clean-up the call, but the client-side will still send a result it produces one which will result in the HubProtocol serializer throwing when trying to resolve the type to deserialize the response to (as the server-side doesn't know about the invocation anymore). When the HubProtocol throws the connection is closed because we have to assume the connections pipe is no longer usable (partial reads etc.).

public Type GetReturnType(string invocationId)
{
if (_hubLifetimeManager.TryGetReturnType(invocationId, out var type))
{
return type;
}
throw new InvalidOperationException($"Unknown invocation ID '{invocationId}'.");

@BrennanConroy BrennanConroy added bug This issue describes a behavior which is not expected - a bug. area-signalr Includes: SignalR clients and servers labels Aug 2, 2022
@rafikiassumani-msft rafikiassumani-msft added this to the 7.0-rc1 milestone Aug 2, 2022
@rafikiassumani-msft rafikiassumani-msft added the Priority:1 Work that is critical for the release, but we could probably ship without label Aug 2, 2022
@BrennanConroy
Copy link
Member Author

BrennanConroy commented Aug 17, 2022

In order to try to promote users to think about timeouts/cancellations when invoking methods on the client from the server, we want to force the user to pass in a CancellationToken, so the API change is to remove the default value from appropriate APIs.

interface ISingleClientProxy
{
-    Task<T> InvokeCoreAsync<T>(string method, object?[] args, CancellationToken cancellationToken = default(CancellationToken));
+    Task<T> InvokeCoreAsync<T>(string method, object?[] args, CancellationToken cancellationToken);
}

static class ClientProxyExtensions
{
-    static Task<T> InvokeAsync<T>(this ISingleClientProxy clientProxy, string method, CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+    static Task<T> InvokeAsync<T>(this ISingleClientProxy clientProxy, string method, CancellationToken cancellationToken);

-    static Task<T> InvokeAsync<T>(this ISingleClientProxy clientProxy, string method, object? arg1, CancellationToken cancellationToken = default(CancellationToken));
+    static Task<T> InvokeAsync<T>(this ISingleClientProxy clientProxy, string method, object? arg1, CancellationToken cancellationToken);

// through 10 args
}

class HubLifetimeManager<THub>
{
-    virtual Task<T> InvokeConnectionAsync<T>(string connectionId, string methodName, object?[] args, CancellationToken cancellationToken = default(CancellationToken));
+    virtual Task<T> InvokeConnectionAsync<T>(string connectionId, string methodName, object?[] args, CancellationToken cancellationToken);
}

class DefaultHubLifetimeManager<THub>
{
-    Task<T> InvokeConnectionAsync<T>(string connectionId, string methodName, object?[] args, CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+    Task<T> InvokeConnectionAsync<T>(string connectionId, string methodName, object?[] args, CancellationToken cancellationToken);
}

class RedisHubLifetimeManager<THub>
{
-    override Task<T> InvokeConnectionAsync<T>(string connectionId, string methodName, object?[] args, CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+    override Task<T> InvokeConnectionAsync<T>(string connectionId, string methodName, object?[] args, CancellationToken cancellationToken);
}

@BrennanConroy BrennanConroy added the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Aug 17, 2022
@ghost
Copy link

ghost commented Aug 17, 2022

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

@halter73
Copy link
Member

I think this API makes sense. APIs like SendAsync which use cancellationToken = default, but the cancellation only applies to connection backpressure. In that case a completely unresponsive client would be timed out by retransmit limits or min rate limits.

In the case of these InvokeAsync APIs, the client may still be responsive at the connection level and avoid any automatic timeout if there's a bug in the client application that causes it not to return a value in a timely matter.

I think forcing people manually pass in a CancellationToken is a good idea. Hopefully it will make developers at least think about the need for app-level timeouts. If someone really wants to wait indefinitely, it's not hard to pass in CancellationToken.None. It's more self-documenting that way. Any objections?

@halter73 halter73 removed the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Aug 19, 2022
@halter73
Copy link
Member

API Approved!

@halter73 halter73 added the api-approved API was approved in API review, it can be implemented label Aug 19, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Sep 20, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-signalr Includes: SignalR clients and servers bug This issue describes a behavior which is not expected - a bug. Priority:1 Work that is critical for the release, but we could probably ship without
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants