When a client cancels an in-flight gRPC streaming call mid-run, what does the server-side handler observe and have to do?
answer
- either side, any moment
- whole call, not one direction
- reads and writes start failing
- the handler must check between units
- cancelled is a normal path, not an exception
basics
~20 sCancelling aborts the whole call in both directions. The server's handler stops receiving messages and its reads and writes on that call start failing as cancelled; it must notice, stop working and release what the call held. No response is delivered.
solid answer
~50 sCancellation is a call-level abort available to **either** side at any moment, and it is the opposite of a half-close: half-close finishes one direction of a healthy call, a cancel tears the whole call down. When the operator abandons the run, the client's call ends immediately with `CANCELLED (1)`; on the server, the handler's next read returns no more messages and any write it attempts on that call fails. A handler that is busy computing and never touches the call again notices nothing and keeps burning CPU until it does — which is why long streaming handlers should check the call's cancelled state between units of work. The server can also end a call early from its side by sending a status; the client's further sends then fail. One call, one ending, whoever triggers it.
go deeper
Know that either side can abort a call in progress, that the whole call ends rather than one direction, and that the caller sees a cancelled outcome instead of a response.
Explain what the peer sees: reads stop returning messages and writes on that call fail. Contrast it with a half-close, where the read loop finishes cleanly and a response is still expected.
Bring the operational half: a handler that is computing rather than reading learns nothing until it next touches the call, so long handlers must check the cancelled state between units of work or burn capacity on output nobody reads.
The tradeoff to own is that cancellation stops delivery, never side effects. Any system where abandoned calls are routine needs an explicit answer for the work already committed before the abort.
A streaming call can run for hours, so both ends need a way to stop it early. **Cancellation** is that way: either side can abort an in-flight call at any point, and the abort applies to the whole call — both directions, immediately, with no response delivered. ## What the cancelling side does When the operator abandons a run mid-field, the client cancels the call it is holding. From the client's point of view the call is over at once: it stops sending, it will receive nothing more, and the call's outcome is `CANCELLED (1)`. There is no negotiation and nothing to wait for. Any partial work — three hours of samples already sent — is simply part of a call that ended without a result. ## What the peer observes On the server, the abort surfaces through the call itself rather than as an event: - A read that was waiting for the next request message stops waiting and reports that the call is gone, not that the sequence ended normally. This is the crucial distinction from a half-close, where the read loop finishes cleanly and the handler is expected to answer. - Any write the handler attempts on that call fails. There is nowhere to send a response, because the client is no longer listening for one. - The call's cancelled state is observable, so a handler that is not blocked on a read can ask whether the call is still alive. The handler's job is then narrow and unglamorous: **stop, and let go.** Abandon the partial computation, release buffers, close the file or the transaction the upload was accumulating into, and return. Trying to send a final "upload aborted" message is pointless — there is no live direction left to carry it. ## Cancel, half-close and an early server status | Event | Triggered by | Request direction | Response direction | Outcome seen by the caller | |---|---|---|---|---| | Half-close | the sending side | finished cleanly | still open | not decided yet | | Cancel | either side | aborted | aborted | `CANCELLED (1)` | | Server ends early with a status | the server | aborted | finished | whatever status the server sent | | Deadline expires | the passage of time | aborted | aborted | `DEADLINE_EXCEEDED (4)` | The third row is worth dwelling on, because it is the mirror image of the client's cancel. A server handler for a client-streaming upload can decide after the fortieth sample that the machine identifier is wrong, and end the call right there with a status instead of reading the remaining ten thousand messages. The client's next send on that call fails, and it learns the call is over. Ending a call early is therefore a legitimate server-side tool, not only an error path — it is how a server refuses work without waiting for the client to finish offering it. ## The failure mode: a handler that never notices Cancellation only helps if somebody observes it. The hard case is a handler that is not waiting on a read at all — it has taken the last five thousand samples and is grinding through a recalculation that takes ninety seconds. The call was cancelled sixty seconds ago; nothing in the handler knows, because nothing in the handler has touched the call since. The work completes, the write fails, and the server has spent ninety seconds of CPU on output nobody will ever read. On a fleet of machines whose operators abandon runs routinely, that is real capacity burned. The discipline that fixes it: 1. Check the call's cancelled state between units of work in any long-running handler, and return early when it is set. 2. Treat cancellation as the ordinary path, not an exception — abandoned runs, closed dashboards and users navigating away are all normal. 3. Make the cleanup idempotent and immediate: whatever the call accumulated has to be discardable without a final message. ## What cancellation is not - **Not a connection close.** The long-lived connection carries many concurrent calls; cancelling one leaves the rest running untouched. - **Not a rollback.** Side effects the server already performed are still performed. Undoing them is the application's problem, not the protocol's. - **Not a pause.** There is no resume. Continuing means a new call, and a new call starts with nothing. - **Not something only clients can do.** Either side ends a call early; the server's version is sending a status. The senior signal in this answer is the second-order point: the protocol delivers the cancel, but only a handler that is written to look for it converts that into work actually stopping.
- How is a cancel different from the client simply half-closing and walking away?A half-close tells the server the request sequence finished normally, so the handler will compute and try to send a response — exactly the work you wanted to avoid. A cancel aborts both directions and tells the handler the result is unwanted. Half-closing and abandoning the call wastes the server's work; cancelling stops it.
- Can the server abort a client-streaming upload before the client has finished sending?Yes. The server ends the call by sending its status at any point, for instance when an early message is invalid. The client's subsequent sends on that call fail and it sees the status the server chose. This is how a server refuses work without reading the rest of the sequence.
- Does cancelling one call disturb the other calls in flight?No. Cancellation is scoped to a single call. Many calls run concurrently over the same long-lived connection, and aborting one has no effect on the others or on the connection itself.
saying these in an interview costs you the question
- Thinks cancelling a call closes the long-lived connection
- Expects the server to finish and return its response anyway
- Uses half-close as the way to abandon unwanted work
- Believes only the client can end a call early
- Assumes a busy handler notices the cancel without checking
- Treats cancellation as an exceptional error rather than a normal ending