Use this file to discover all available pages before exploring further.
A Saga is a design pattern for handling transactions that span multiple services. It breaks the process into a sequence of local operations, each with a corresponding compensating action. If a failure occurs partway through, these compensations are triggered to undo completed steps, ensuring your system stays consistent even when things go wrong.
A travel booking workflow: book a flight, rent a car, then book a hotel. If any step fails (e.g. hotel full), we roll back previous steps to maintain consistency.Implementation:
Wrap business logic in a try-block, throw terminal errors for compensation cases
Add compensations to a list for each step
In catch block, run compensations in reverse order and rethrow
This pattern is implementable with any of our SDKs. We are still working on translating all patterns to all SDK languages.
If you need help with a specific language, please reach out to us via Discord or Slack.
Restate automatically retries transient failures (network hiccups, temporary outages). For non-transient failures, sagas are essential:
Business logic failures: When failures are business decisions (e.g. “Hotel is full”), retrying won’t help. Throw a terminal error to trigger compensations.
User/system cancellations: When you cancel long-running invocations, sagas undo previous operations to maintain consistency.
Sagas in Restate are flexible and powerful since they’re implemented in user code. However, you need to make sure compensations are idempotent.
The example uses customer ID for idempotency, preventing duplicate bookings on retries. The API provider deduplicates requests based on this ID.Different APIs require different approaches:
Two-phase APIs: First reserve, then confirm or cancel. Register the compensation after reservation, when you have the resource ID.
This type of API usually auto-cancels reservations after a timeout.
const bookingId = await ctx.run(() => flightClient.reserve(customerId, flight));compensations.push(() => ctx.run(() => flightClient.cancel(bookingId)));// ... do other work, like reserving a car, etc. ...await ctx.run(() => flightClient.confirm(bookingId));
One-shot APIs with idempotency key: Generate idempotency key, persist in Restate, register compensation (e.g. refund), then do action (e.g. charge). Register compensation first in case action succeeded but confirmation was lost.