Skip to content

Feature: durable_async!() macro for inline task scopes #54

@affandar

Description

@affandar

Summary

Introduce a durable_async!() macro that wraps an async block into a DurableFuture, enabling it to be used in select2() and join() alongside other durable futures.

Motivation

In tokio, you can race arbitrary async blocks:

tokio::select! {
    res = single_operation() => { ... }
    res = async {
        let a = step1().await;
        let b = step2(a).await;
        b
    } => { ... }
}

In duroxide, select2() only accepts DurableFuture, so users must create sub-orchestrations to race groups of sequential operations. This is verbose and scatters workflow logic.

Proposed Solution

let (winner, _) = ctx.select2(
    ctx.schedule_activity("Fast", input.clone()),
    durable_async!(ctx, {
        let a = ctx.schedule_activity("Step1", input).into_activity().await?;
        let b = ctx.schedule_activity("Step2", a).into_activity().await?;
        Ok(b)
    }),
).await;

Key Features

  • Inline definition: No pre-registration required
  • Same instance: Runs in the same orchestration, same history
  • Full control flow: Supports if, for, match, loop, continue, break, early return
  • Scoped cancellation: When used in select2, losing branch activities are cancelled
  • Helper functions: Can call async helper functions that use the context

Implementation Overview

  1. Add Kind::Task variant to DurableFuture
  2. Add scope tracking to CtxInner (scope stack, activity-to-scope mapping)
  3. Implement durable_async! macro
  4. Update AggregateDurableFuture to cancel loser scope activities

Example Use Cases

  • Racing a quick check against a multi-step validation pipeline
  • Timeout around a complex provisioning sequence
  • Racing two alternative processing paths

Related

  • Proposal document: proposals/durable-async-macro.md
  • Activity cancellation uses the same provider mechanism (queue deletion)

Tasks

  • Add Kind::Task variant and DurableOutput::Task
  • Implement scope tracking in CtxInner
  • Implement begin_scope(), end_scope(), register_activity_in_scope()
  • Implement durable_async! macro
  • Implement poll() for Kind::Task
  • Update AggregateDurableFuture for scope cancellation
  • Add unit tests for scope tracking
  • Add integration tests for select2 with durable_async!
  • Add cancellation tests
  • Add replay determinism tests
  • Update documentation

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions