add a flag to ServerCallStreamObserver to make onCompleted() throw StatusRuntimeException if the call was cancelled
See original GitHub issueIs your feature request related to a problem?
It is currently a bit cumbersome If a gRPC method needs to attempt to roll back effects of a call when the client cancels and work is not dispatched to other threads:
responseObserver.onNext(...)
will not throw an exception neither in unary methods (they don’t throw on cancel by design) nor in server-streaming (due to issue #8409 ). FurthermoreresponseObserver.onNext()
may never be called by some server-streaming methods that return a stream of length 0 but need to attempt to roll back side-effects of processing client’s request nevertheless.onCancelHandler
cannot be used as it will be called only after the method exits, due to listener’s “called by at most 1 thread at a time” contract.
Describe the solution you’d like
draft: https://github.com/morgwai/grpc-java/commit/f26b7db916f2cf5c08f4d0161586ad78ba998d6a If the general idea of this feature request is accepted, I will be happy to prepare a proper PR myself starting from the above draft.
Describe alternatives you’ve considered
Currently in cases like this, the easiest solution is to keep checking responseObserver.isCancelled()
before responseObserver.onCompleted()
(and in case of server-streaming calls possibly additionally before each responseObserver.onNext()
if it’s desirable to interrupt processing ASAP). This however is kinda C-style: client cancelling is an exceptional situation, so I think it’s cleaner to handle it in a catch block rather and keep the main positive code-path clean.
Context.addListener()
can probably be also used, but as the listener will be called by another thread, it will have to set a specially designated volatile/synchronized flag, which the code of the main positive case will need to examine before responseObserver.onCompleted()
. Therefore it’s not better than the above solution using responseObserver.isCancelled()
.
Additional context
Even for methods that do dispatch work to other threads, using exception rather than onCancelHandler
is a cleaner solution in some cases: as cancellation may occur in the middle of a call, onCancelHandler
similarly as a listener set by Context.addListener()
described above, often needs to set a specially designated volatile/synchronized flag to stop further processing, that again needs to be checked by the code of the main positive case, while an exception interrupts the main positive code-path with much less hassle.
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (8 by maintainers)
Top GitHub Comments
Okay. In that case then the onCancelHandler is your best bet. It is the most reliable notification of cancellation. And again, cancellation can occur after the server calls
onCompleted()
. If you are waiting to commit the transaction locally whenonCompleted()
is called, then #5895 would be a better approach. You’d prepare the transaction within the service handler and callstreamObserver.onComplete()
. IfonCancelHandler
fires, cancel the transaction. If (TBD)onCompleteHandler
fires, commit the transaction.Wanting an exception when calling
onCompleted()
is just broken, and has nothing to do with “c-style” or not. Async APIs generally don’t throw, because they’ve not done the operation yet! Normal Java practice for an async API (e.g., one that returns a Future) is to not throw and instead notify of the failure asynchronously (like failing the Future).I sent out #8449 to document “things are async and can take a while.” I didn’t call out this specific detail about
onCompleted()
. That can be done as part of #5895.