Skip to main content

activity

Attribute Macro activity 

Source
#[activity]
Expand description

Marks an async method as a durable activity (external side effects).

Use #[activity] for operations that have external side effects and need to be retried on failure. Activities are called from within workflows via DurableContext::run().

Do NOT use #[activity] for state mutations — use #[workflow] instead.

§Usage

#[entity_impl]
#[state(OrderState)]
impl OrderProcessor {
    /// Send confirmation email - external side effect
    #[activity]
    async fn send_email(&self, to: String, body: String) -> Result<(), ClusterError> {
        email_service.send(&to, &body).await
    }

    /// Charge payment - external API call
    #[activity]
    async fn charge_payment(&self, card: Card, amount: u64) -> Result<PaymentId, ClusterError> {
        payment_gateway.charge(&card, amount).await
    }

    /// Main workflow that uses activities
    #[workflow]
    async fn process(&self, ctx: &DurableContext, order: Order) -> Result<(), ClusterError> {
        // Activities are called via ctx.run() for persistence and retry
        ctx.run(|| self.charge_payment(order.card, order.total)).await?;
        ctx.run(|| self.send_email(order.email, "Order confirmed!")).await?;
        Ok(())
    }
}

§When to Use

  • External API calls (payment, email, webhooks)
  • Operations that may fail transiently and need retry
  • Side effects that should only happen once per workflow execution

§Visibility

Activities are #[private] by default — they can only be called internally or from workflows via DurableContext. They cannot be made #[public].

§Idempotency Key

Use #[activity(key = |req| ...)] to deduplicate:

#[activity(key = |to, _body| to.clone())]
async fn send_email(&self, to: String, body: String) -> Result<(), ClusterError> {
    // Only sends once per recipient within the workflow
}