use durare::{DurableContext, DurableEngine, InMemoryProvider, Result, WorkflowOptions};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, OnceLock};
use std::time::Duration;
struct PricingService {
api_base: String,
fee_bps: u64, }
impl PricingService {
fn quote(&self, amount_cents: u64) -> u64 {
CALLS.fetch_add(1, Ordering::SeqCst);
println!(
" >> POST {}/quote ({amount_cents}c @ {}bps)",
self.api_base, self.fee_bps
);
amount_cents * self.fee_bps / 10_000
}
}
static DEPS: OnceLock<PricingService> = OnceLock::new();
static CALLS: AtomicUsize = AtomicUsize::new(0);
#[durare::step]
async fn compute_fee(ctx: &DurableContext, amount_cents: u64) -> Result<u64> {
let deps = DEPS.get().expect("deps set at startup");
Ok(deps.quote(amount_cents))
}
#[durare::workflow]
async fn checkout(ctx: DurableContext, amount_cents: u64) -> Result<u64> {
let fee = compute_fee(&ctx, amount_cents).await?;
Ok(amount_cents + fee)
}
#[tokio::main]
async fn main() -> Result<()> {
DEPS.set(PricingService {
api_base: "https://pricing.internal".to_string(),
fee_bps: 250,
})
.unwrap_or_else(|_| unreachable!("DEPS is set exactly once at startup"));
let engine = DurableEngine::new(Arc::new(InMemoryProvider::new())).await?;
engine.launch().await?;
let total: u64 = engine
.start_with(Checkout, 10_000u64, WorkflowOptions::with_id("checkout-1"))
.await?
.await?;
println!(
"[run 1] total = {total}c (dependency calls: {})\n",
CALLS.load(Ordering::SeqCst)
);
let total_again: u64 = engine
.start_with(Checkout, 10_000u64, WorkflowOptions::with_id("checkout-1"))
.await?
.await?;
println!(
"[run 2] total = {total_again}c (dependency calls: still {})",
CALLS.load(Ordering::SeqCst)
);
assert_eq!(total, total_again);
assert_eq!(
CALLS.load(Ordering::SeqCst),
1,
"replay must not re-invoke the dependency"
);
println!(
"\n[ok] dependency lives in a global, is read inside a step, and runs \
exactly once — the replay uses the checkpoint"
);
engine.shutdown(Duration::from_secs(1)).await?;
Ok(())
}