How to implement cancellation in Request Reply Pattern in .NET?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
Cancellation in a request-reply flow is trickier than cancellation in a local method call. In .NET, a CancellationToken can stop the caller from waiting, but in a distributed request-reply pattern you also need to decide what happens to the in-flight request, the pending reply registration, and possibly the remote worker that is still doing the work.
Separate Local Cancellation From Remote Cancellation
The first design decision is important: canceling the caller's wait is not automatically the same thing as canceling the remote operation.
There are two common meanings of "cancel":
- Stop awaiting the reply locally and clean up the pending request entry
- Send an explicit cancel message so the remote worker can stop if it has not finished
Many systems implement the first reliably and the second only when the business process really needs it.
Track Pending Replies by Correlation ID
A practical .NET implementation keeps a dictionary of pending requests keyed by a correlation ID. Each request gets a TaskCompletionSource that will be completed by the reply handler or by cancellation.
This is the core structure that lets the request sender await a reply asynchronously without blocking a thread.
Register the Cancellation Token
When you send the request, register the token so cancellation removes the pending request and completes the task as canceled.
A few details matter here:
- '
RunContinuationsAsynchronouslyprevents reply handlers from running caller continuations inline' - The dictionary entry is removed on cancellation
- An optional cancel message is sent to the remote side
If your system does not support remote cancellation, remove the cancel publish and just cancel the local wait.
Complete the Pending Request When the Reply Arrives
The reply handler looks up the correlation ID and completes the matching task.
This completion must be idempotent. If cancellation already removed the entry, the reply should be ignored or logged rather than treated as a failure.
Decide What the Remote Side Should Do
If you publish explicit cancel messages, the worker has to cooperate. That usually means the worker keeps its own map of active operations and observes a token or canceled-state flag during long-running work.
Without that extra design, cancellation remains local-only: the caller stops waiting, but the remote process still finishes and its reply is discarded when it returns.
That behavior is often acceptable for read-only queries, but it may be wasteful for expensive operations.
Common Pitfalls
- Assuming
CancellationTokenalone cancels remote work is incorrect. It only cancels code that actually observes that token. - Forgetting to remove pending requests on cancellation creates memory leaks and eventual reply-routing bugs.
- Treating a late reply as an error instead of a normal race condition makes distributed cancellation noisier than it needs to be.
- Not deciding whether cancellation is local-only or protocol-level leaves the behavior ambiguous for callers and responders.
Summary
- In request-reply systems, cancellation has both local waiting semantics and optional remote-stop semantics.
- Track pending replies by correlation ID with a
TaskCompletionSource. - Register the
CancellationTokento remove the pending entry and cancel the awaiting task. - If the business flow requires it, send an explicit cancel message so the responder can cooperate too.
Related reading
- How to implement contract testing when kafka is involved in microservice architecture?
- how to implement eigenvalue calculation with MapReduce/Hadoop?
- How to implement LFU cache using STL?
- How to introduce delay in rebalancing in case of kafka consumer group?
- How to implement .get feature with FutureTask or BackgroundTask using android?
- How to implement lock-free skip list
- How to implement Oplock opportunistic locking in .NET?
- How to include more outcomes on Infer.NET's BPM?

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.