How it works
The pattern uses two services:CodingAgent— a Virtual Object, one per agent session. It holds the conversation history and the invocation ID of any running task.CodingTask— a long-running Service that performs the actual work (a lengthy LLM call, or a chain of them).
- The agent’s
messagehandler loads the conversation history from the Virtual Object state. - If a task is already running, it cancels that invocation and waits for the cleanup to finish.
- It sends a new task to the
CodingTaskservice and persists the new invocation ID. - Inside
CodingTask, the cancellation surfaces as a terminal error at the next Restate await. The task catches it, notifies the orchestrator for durable cleanup, and re-raises so Restate records the invocation ascancelled.
How does Restate help?
- Durable cancellation signals: Cancellation is a first-class, durable signal that propagates through nested invocations automatically.
- Cleanup always runs: Terminal errors surface at the next Restate await, giving the handler a chance to run durable cleanup steps before finishing.
- Concurrency control per session: Virtual Objects queue concurrent requests per key, so interruptions are processed one at a time without races.
- Observability: The Restate UI shows the cancelled invocation side-by-side with the one that replaced it.