Search code examples
language-agnosticgrpcipccancellation

Best Practice: "Soft Cancellation" with gRPC


It seems to me that the built in cancellation support of gRPC is quite aggressive in the following sense: If I invoke cancellation on client side, the communication channel is closed immediately. The server gets informed and can do cleanup work, but there seems to be no chance to inform the client after cleanup has been finished.

What am I supposed to do if I want the following "soft cancellation" behavior?

  1. Client requests cancellation
  2. Server receives cancellation request and starts cleanup
  3. Server finishes cleanup and closes the communication channel
  4. Client gets informed that cancellation procedure has been finished

This behavior could be achieved using a request stream and the oneof keyword:

service ServiceName{
    rpc Communicate (stream Request) returns (Response);
}

message Request {
    oneof request_oneof {
        ActualRequest actual = 1;
        CancellationRequest cancellation = 2;
    }
}

This should not be too hard to implement, but it also looks quite cumbersome. What do you think is the intended way?


Solution

  • For unary RPCs, there is not any alternative to cancellation. You'd need to make it a client-streaming RPC in order to encode the additional communication events.

    For client-streaming and bidi-streaming RPCs, a common pattern is for the client to delay half-close. When the client is done, the client half-closes, which notifies the server the client is done. The server then performs clean up and can close the stream cleanly with a status message. You could do the same approach, making use of half-close:

    service ServiceName {
        rpc Communicate (stream ActualRequest) returns (Response);
    }
    
    message ActualRequest {...}
    

    Streaming allows you to make custom protocols like this, but nothing but your application will know how to interact with that protocol. A proxy, for example, could not initiate a "graceful cancellation" for this protocol. So you should only use streaming when you really need it and you should still properly implement the "hard" cancellation even though you may not use it as often.