Skip to content

Forward unmatched external events to running sub-orchestrations #21

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions