Skip to content

Forward unmatched external events to running sub-orchestrations #21

@pinodeca

Description

@pinodeca

Forward unmatched external events to running sub-orchestrations

Problem

Client::raise_event(instance_id, name, data) only delivers the event to the orchestration whose ID is instance_id. If a schedule_wait(name) is awaiting inside a sub-orchestration spawned by schedule_sub_orchestration (e.g. parallel branches via join / select), an event raised on the parent never reaches those waiters.

From a caller's perspective the sub-orchestration is an implementation detail — the auto-generated IDs ({parent}::sub::{event_id}) aren't part of the public contract and aren't known to external signalers, so there's no practical way for a client to target the child directly.

Repro sketch

// orchestration
ctx.schedule_sub_orchestration("child", input).await?; // child awaits schedule_wait("approve")

// caller
client.raise_event(parent_id, "approve", data).await?; // never observed by child

Workarounds today

Callers can walk get_instance_tree(parent) and raise_event on each running descendant, but this has races:

  • Descendant may be Pending (not yet Running) when enumerated and gets skipped.
  • Event may arrive before the sub-orchestration is even scheduled; it then lands on the parent's pending queue with no consumer.
  • Broadcasts to every descendant regardless of whether they actually subscribe to that event name.

Proposed direction

Some options worth considering — happy to discuss which fits the model best:

  1. Inheritance on miss: an unmatched raise_event on a parent is buffered and offered to sub-orchestrations as they reach a matching schedule_wait. Symmetric to how schedule_wait already buffers events that arrive before the wait is registered.
  2. Explicit broadcast API: Client::raise_event_tree(root, name, data) (or a flag on raise_event) that durably fans the event out to the root and all current/future descendants until they complete.
  3. Subscription registration: sub-orchestrations register a "forward name from parent" subscription when they call schedule_wait, and the runtime routes parent events accordingly.

Option 1 is the most ergonomic for callers since they don't need to know the topology. Option 2 is the smallest API change but pushes timing responsibility back to the client.

Why this matters

Any orchestration pattern that fans out work into sub-orchestrations and then expects a human-in-the-loop or external system to signal one of those branches is currently unreachable without out-of-band coordination. Approval flows inside parallel branches, cancel-style signals targeting a specific competitor in a race, etc.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions