Skip to main content

workflow

Attribute Macro workflow 

Source
#[workflow]
Expand description

Marks an async method as a durable workflow (state mutations).

Use #[workflow] for any method that modifies entity state via state_mut(). Workflows are persisted and can be replayed on restart, ensuring state changes are never lost.

§Usage

#[entity_impl]
#[state(CounterState)]
impl Counter {
    #[workflow]
    async fn increment(&self, amount: i32) -> Result<i32, ClusterError> {
        let mut state = self.state_mut().await;
        state.count += amount;
        Ok(state.count)
    }

    #[workflow]
    async fn reset(&self) -> Result<(), ClusterError> {
        let mut state = self.state_mut().await;
        state.count = 0;
        Ok(())
    }
}

§When to Use

  • Any method that calls state_mut() should be #[workflow]
  • State mutations are atomic and persisted
  • If the process crashes, the workflow will be replayed

§Complex Workflows with Activities

For workflows that need to perform external side effects (API calls, emails, etc.), use #[activity] methods and call them via DurableContext:

#[workflow]
async fn process_order(&self, ctx: &DurableContext, order: Order) -> Result<Receipt, ClusterError> {
    // State mutation
    {
        let mut state = self.state_mut().await;
        state.orders.push(order.id);
    }
     
    // External side effect (retryable, persisted)
    ctx.run(|| self.send_confirmation_email(&order)).await?;
     
    Ok(Receipt::new(order.id))
}

§Visibility

By default, #[workflow] methods are #[public] (externally callable).

§Idempotency Key

Use #[workflow(key = |req| ...)] to deduplicate repeated calls:

#[workflow(key = |order| order.id.clone())]
async fn process_order(&self, order: Order) -> Result<Receipt, ClusterError> {
    // Duplicate calls with same order.id return cached result
}