resonatehq/resonate-skills/resonate-saga-pattern-rust

Claude Code and agent skill by resonatehq/resonate-skills

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Implement saga patterns for distributed transactions in Rust with Resonate — forward steps with compensating actions that unwind on failure using Result<T> and match-based compensation dispatch. Use when coordinating multi-step Rust workflows that need consistency across failures. SDK in active development (0.6.0 on crates.io); API surface may change between releases.

Install

npx vskill@latest install resonatehq/resonate-skills/resonate-saga-pattern-rust

SKILL.md

Resonate Saga Pattern — Rust

SDK note (0.6.0). The Rust SDK is in active development. The saga pattern below uses only documented surface (ctx.run, Result<T>, ? propagation). Verify against the current SDK source before shipping.

Overview

A saga is a long-running transaction split into smaller steps, each with a compensating action. Forward steps run top-to-bottom; on failure, compensations run bottom-to-top. Rust expresses this naturally via Result<T> + ? for forward propagation and a tracked "completed" list for compensation dispatch.

For the language-agnostic mental model, see resonate-saga-pattern-typescript.

When to use

  • Multi-step workflow where intermediate state is visible to other systems
  • Each step is idempotent or inexpensive to retry individually
  • Compensation logic exists for every committed step
  • You need "all or nothing" consistency without a distributed transaction

Basic shape

rust
use resonate::prelude::*;
use serde::{Serialize, Deserialize};
#[derive(Clone, Serialize, Deserialize)]
enum Step { Inventory, Payment, Shipment }
#[derive(Clone, Serialize, Deserialize)]
struct SagaResult {
status: String,
order_id: String,
compensated: Vec<String>,
}
#[resonate::function]
async fn place_order(ctx: &Context, order_id: String) -> Result<SagaResult> {
let mut completed: Vec<Step> = Vec::new();
// attempt the forward path
let result = try_forward(ctx, &order_id, &mut completed).await;
match result {
Ok(()) => Ok(SagaResult {
status: "success".into(),
order_id,
compensated: vec![],
}),
Err(_err) => {
// compensate in reverse order
let mut comp_names = Vec::new();
for step in completed.iter().rev() {
ctx.run(compensate, (step.clone(), order_id.clone()))
.await?;
comp_names.push(format!("{:?}", step));
}
Ok(SagaResult {
status: "failed".into(),
order_id,
compensated: comp_names,
})
}
}
}
async fn try_forward(
ctx: &Context,
order_id: &str,
completed: &mut Vec<Step>,
) -> Result<()> {
ctx.run(reserve_inventory, order_id.to_string()).await?;
completed.push(Step::Inventory);
ctx.run(charge_payment, order_id.to_string()).await?;
completed.push(Step::Payment);
ctx.run(create_shipment, order_id.to_string()).await?;
completed.push(Step::Shipment);
Ok(())
}
#[resonate::function]
async fn compensate((step, order_id): (Step, String)) -> Result<()> {
match step {
Step::Shipment => { /* cancel_shipment(&order_id) */ Ok(()) }
Step::Payment => { /* refund_payment(&order_id) */ Ok(()) }
Step::Inventory => { /* release_inventory(&order_id) */ Ok(()) }
}
}
#[resonate::function]
async fn reserve_inventory(order_id: String) -> Result<()> { Ok(()) }
#[resonate::function]
async fn charge_payment(order_id: String) -> Result<()> { Ok(()) }
#[resonate::function]
async fn create_shipment(order_id: String) -> Result<()> { Ok(()) }

Each forward step is a durable checkpoint. If the worker crashes mid-saga, Resonate resumes from the last successful yield (in Rust, the last successful .await?).

Compensation must be retryable

A compensation that fails stalls the saga in an inconsistent state:

rust
use std::time::Duration;
for step in completed.iter().rev() {
ctx.run(compensate, (step.clone(), order_id.clone()))
.timeout(Duration::from_secs(600)) // 10 min per compensation
.await?;
}

If a compensation exhausts its timeout/retry, you have a real inconsistency — log and alert.

Explicit step objects via enum dispatch

Rust's enum + match is idiomatic for a small, closed set of saga steps:

rust
#[derive(Clone, Serialize, Deserialize)]
enum ForwardStep { Inventory, Payment, Shipment }
impl ForwardStep {
async fn run(&self, ctx: &Context, order_id: &str) -> Result<()> {
match self {
ForwardStep::Inventory => ctx.run(reserve_inventory, order_id.to_string()).await?,
ForwardStep::Payment => ctx.run(charge_payment, order_id.to_string()).await?,
ForwardStep::Shipment => ctx.run(create_shipment, order_id.to_string()).await?,
};
Ok(())
}
}

For large sets of dynamic steps, prefer Vec<Box<dyn StepTrait>> — but that adds dyn-trait complexity beyond the basic pattern.

Distinct Rust idioms

  • `Result<T>` + `?` handles forward-path propagation naturally — no try/except needed for "raise on first error"
  • Explicit `let result = ... .await` + `match` for the catch-and-compensate branch, since ? would bail before compensation runs
  • `completed.iter().rev()` for reverse iteration — idiomatic Rust, no reversed() builtin needed
  • `enum` + `match` for compensation dispatch — cleaner than string-based switches
  • `(Step, String)` tuple parameter to bundle args into a single serializable input for ctx.run — the SDK serializes the whole argument, so tuples work naturally
  • `Clone` derive on step enums — needed because iter().rev() borrows and compensation needs owned values

Avoid

  • Panicking inside a durable function (panic!, unreachable!, assert!) — panics don't compensate; use Result explicitly
  • Non-idempotent compensations — each compensation may retry
  • Compensations that read the SoR without a fresh ctx.run — the read happens inside a leaf for checkpointing

Related skills

  • resonate-basic-durable-world-usage-rust — ctx.run, builder options, function kinds
  • resonate-recursive-fan-out-pattern-rust — parallel fan-out, can be combined with sagas
  • durable-execution — foundational replay semantics
  • resonate-saga-pattern-typescript / -python — sibling SDKs for comparison

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/resonatehq/resonate-skills/resonate-saga-pattern-rust/badge)](https://verified-skill.com/skills/resonatehq/resonate-skills/resonate-saga-pattern-rust)

Something wrong with this skill or its page? Tell us.