Client-side visibility of server canceling context

42 views
Skip to first unread message

rmar...@hubspot.com

unread,
Feb 20, 2019, 5:08:03 PM2/20/19
to grpc.io

In the context of server streaming or bi-directional streaming calls, I'm under the impression the client should be able to receive notice when the server has decided to cancel the context for the call. I've been trying to write up a test to capture this behavior, but it doesn't seem like canceling the context on the server is triggering anything in the response observer in the client side. Maybe I'm misinterpreting how the client interacts with a server cancellation or just setting up the test case wrong?

My client test call is something like:
ClientResponseObserver<Object, TestResponse> closingObserver = ClosingStreamObserver.closingClientResponseObserver(clientTestCloseable, responseObserver);
serviceStub.testStream(req, closingObserver)
;

and then I wait a bit to see if anything propagates (I've got an atomic boolean that I flip when `onError` or `onCompleted` get called. I had also tried a listener on the current context on the client side as well, but that seemed irrelevant.

On the server side I'm canceling the context something like this:
public void testStream(TestRequest request, StreamObserver<TestResponse> responseObserver) {
CancellableContext cancellableContext = Context.current().withCancellation();
cancellableContext.cancel(new RuntimeException("Server context canceled"));
}

To step back a bit, I'm looking to know if and how a server can signal it is canceling a request to the client.

Carl Mastrangelo

unread,
Feb 21, 2019, 1:50:23 PM2/21/19
to grpc.io
I think you need to call `attach()` on the context to install it (rather than just construct it).  Normally this sticks it in a thread local which gRPC then reads from.

rmar...@hubspot.com

unread,
Feb 21, 2019, 2:20:54 PM2/21/19
to grpc.io
Thanks for the reply. I updated my test server method to the following (hopefully I understood correctly), although I observed no differences in behavior on the client-side:

public void testStream(TestRequest request, StreamObserver<TestResponse> responseObserver) {
  Context originalContext = Context.current();
CancellableContext cancellableContext = originalContext.withCancellation();
cancellableContext.attach();
cancellableContext.detachAndCancel(originalContext, new RuntimeException("Server context canceled"));
}

It's the ClientResponseObserver that I'm inspecting to observe the cancellation, I suppose maybe expecting `onError` to get called on behalf of the server cancellation.

Carl Mastrangelo

unread,
Feb 21, 2019, 8:21:49 PM2/21/19
to grpc.io
Let me re-establish somethings just to make sure we are on the same page.

1.  Servers are always async.   Server Stubs are always passed a Response observer.   For Bidi and Client Streaming, they also return a Request Observer.
2.  Servers are usually on the receiving end of cancellation.  Servers find out about cancellation by inspecting the Context.current().isCancelled() value.
3.  Servers can also check for early termination of the RPC for Bidi and ClientStreaming by getting onError() called on the Request observer.

4.  Clients usually don't check for cancellation.  Client's find out the RPC is prematurely terminated by getting an onError() callback with a StatusRuntimeException or StatusException with a status Code.  From the client's POV, server cancellation and regular failure look the same.
5.  Clients using the blocking API usually use Context cancellation to end the call early.   (because there is no other way)
6.  A blocking client will take the current context, wrap a cancellable context around it, and install the new ctx back into the current.  (This was the attach() suggestion I made above).  When the RPC is complete, they swap the old context back into place.
7.  ClientResponseObserver is probably not what you want here.  It's meant to modify flow control on the client stub.  It's rarely used.

With these in mind, It seems like your server should just do responseObserver.onError(Status.CANCELLED.withDescription("Server is cancelling").asRuntimeException());  The client be notified by the onError() invocation on it's response observer.  (I think you said it's bidi, so this would be the observer "returned" from your client stub.

Does this make sense?
Reply all
Reply to author
Forward
0 new messages