Skip to content

Parallel Workflows

Sayiir supports fork/join parallelism, allowing you to run multiple branches in parallel and collect their results in a join task. This pattern is ideal for independent operations that can execute concurrently, such as validating payment while checking inventory.

The fork/join pattern splits execution into multiple parallel branches, then merges the results:

from sayiir import task, Flow, run_workflow
@task
def validate_payment(order: dict) -> dict:
return {"payment": "valid"}
@task
def check_inventory(order: dict) -> dict:
return {"stock": "available"}
@task
def finalize(results: dict) -> str:
return f"Order complete: {results}"
workflow = (
Flow("checkout")
.fork()
.branch(validate_payment)
.branch(check_inventory)
.join(finalize)
.build()
)
result = run_workflow(workflow, {"order_id": 1})

Rust also supports a concise macro syntax for fork/join:

workflow! {
validate_payment || check_inventory => finalize
}

The || operator denotes parallel execution, and => connects the fork to the join handler.

In Python, you can chain multiple steps within a single branch using .branch(step_a, step_b):

workflow = (
Flow("complex_checkout")
.fork()
.branch(validate_payment, charge_card)
.branch(check_inventory, reserve_items)
.branch(calculate_shipping, book_courier)
.join(finalize_order)
.build()
)

Each branch executes its steps sequentially, but branches run in parallel with each other.

The join task receives a dictionary (Python) or map (Rust) containing the results from all branches. Each entry is keyed by the branch’s task name:

@task
def finalize(results: dict) -> str:
payment = results["validate_payment"]
inventory = results["check_inventory"]
shipping = results["calculate_shipping"]
return f"Order processed: payment={payment}, stock={inventory}, shipping={shipping}"

The join task only executes after all branches complete successfully. If any branch fails, the entire fork/join fails and triggers workflow retry logic.