Skip to main content

Journaling Results

Restate uses an execution log for replay after failures and suspensions. This means that non-deterministic results (e.g. database responses, UUID generation) need to be stored in the execution log, so they can be replayed.

You can use ctx.run to do this.

Here is an example of a database request for which the string response is stored in Restate:

async def do_db_request():
# ... implement ...
return "my_result"
result = await ctx.run("database request", do_db_request)

You cannot use the Restate context within ctx.run. This includes actions such as getting state, calling another service, and nesting other journaled actions.

You can return any payload that can be serialized with bytes(json.dumps(obj), "utf-8") and deserialized with json.loads(buf). If not, you need to specify a custom serializer.

Immediately await journaled actions

Always immediately await ctx.run, before doing any other context calls. If not, you might bump into non-determinism errors during replay, because the journaled result can get interleaved with the other context calls in the journal in a non-deterministic way.

Failure semantics

Failures in ctx.run have the same semantics as failures in the rest of your handler code. By default, the ctx.run function is retried infinitely on failure, unless you specify differently in the retry policy (see below) or throw a TerminalError.

Run-block Retry Policies

You can configure the retry policy for the ctx.run block:

try:
await ctx.run("write", lambda: write_to_other_system(),
# Max number of retry attempts to complete the action.
max_attempts=3,
# Max duration for retrying, across all retries.
max_retry_duration=timedelta(seconds=10)
)
except TerminalError as err:
# Handle the terminal error after retries exhausted
# For example, undo previous actions (see sagas guide) and
# propagate the error back to the caller
raise err

This way you can override the default retry behavior of your Restate service for specific operations.

If you set a maximum number of attempts, then the ctx.run block will fail with a TerminalError once the retries are exhausted. Have a look at the error handling docs and Sagas guide to learn how to handle these.