#[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
}