Durable Agents with Restate
A one-module sample course on durable execution for AI agents with Restate. You kill a running agent on purpose, read the journal Restate kept, and count the model calls the replay paid for. Then you watch the same bug turn into a journal mismatch (RT0016), fix the replay with one line, and kill the fixed agent mid-call to find the window that line cannot close. Every number on these pages comes from a recorded run.
About This Course
An agent is a loop that asks a model what to do, does it, and asks again. Kill the process in the middle and something has to remember how far it got. Restate keeps a journal of each invocation: the input, every durable step and its result, every timer. When the process comes back, Restate replays the handler against that journal, skipping the steps it already has.
Restate can only replay what went through its context. A model call made with a plain HTTP request never reaches the journal, so the replay runs it again, and you pay for it again. If the model answers differently on replay, the handler diverges from the recorded journal and that attempt fails with a journal mismatch (RT0016). Restate retries it under the handler's retry policy: a later attempt may take the recorded path and complete, or the retries run out and the invocation pauses. This module shows all of that on a real Restate server, with the call counts, the journals and the error text, and ends on the rule that prevents it and the window a journaled call still leaves open.
The lab runs locally: a Restate server in Docker, a small Python agent, and a stub standing in for the model so every call can be counted.
Course design original to SciMigo. Pages link to and paraphrase Restate's documentation (docs.restate.dev and restate.dev, as fetched 2026-09-16); no documentation text is reproduced. Every observed number comes from SciMigo's demo repository, run against restate-server 1.7.10 with restate-sdk 1.0.5. Restate is developed by Restate GmbH; this course is independent and not endorsed by Restate.
Prerequisites
- Comfortable Python, including async/await basics
- Docker installed locally; no prior Restate
What You Will Learn
- Explain what Restate's journal records for an invocation, and what a replay re-runs after the process dies
- Predict the extra model calls a crash costs with and without ctx.run, and verify the count, including the call a journaled agent still repeats when it dies mid-call
- Recognize a journal mismatch (RT0016) as replayed code diverging from the recorded journal, and trace how an unrecorded model decision causes it
- Choose how to get a stuck invocation out: resume it on a journal-compatible fix, or kill it and restart it as new
Terminology Mapping
How classic concepts map to the terminology used in this course.
| Classic | This Course (Python) |
|---|---|
| Invocation | one call of a handler, e.g. POST /WeatherAgent/run/send; has an invocation id (inv_…) |
| Durable step | await ctx.run_typed("call model", ask_model, messages=messages) |
| Durable timer | await ctx.sleep(timedelta(seconds=6), name="rate-limit pause") |
| Journal | the sys_journal table (admin API /query) or the Restate UI's invocation timeline |
| Deterministic helpers | ctx.random(), ctx.uuid(), await ctx.time() |
| Invocation retry policy | restate.Service(..., invocation_retry_policy=InvocationRetryPolicy(max_attempts=20, on_max_attempts="pause")) |
Your Learning Path
One self-contained module, about 90 minutes, to work through from top to bottom.