Raxol.Workflow.Async (Raxol v2.6.0)

View Source

Asynchronous execution surface for Raxol.Workflow.Compiled graphs.

Wraps Raxol.Workflow.Runtime.invoke/3 in a separately-spawned process and a per-run telemetry handler so callers can either:

This module ships the synchronous-spawn shape. A Raxol.Workflow.RunSupervisor (DynamicSupervisor) for production hardening is a follow-up; the API here is supervisor-agnostic, so swapping to a supervised spawn does not break existing callers.

The runtime emits telemetry events on [:raxol, :workflow, :run, _] and [:raxol, :workflow, :node, _]; stream_events/3 attaches a handler scoped to its run_id and converts each event into a CloudEvent before pushing it onto the stream.

Summary

Functions

Spawn the run in a separate process and return a handle immediately.

Spawn a resume in a separate process and return a handle immediately.

Resume a run and return a lazy Stream of CloudEvent structs.

Run the graph and return a lazy Stream of CloudEvent structs.

Functions

async_invoke(compiled, initial_state, opts \\ [])

@spec async_invoke(Raxol.Workflow.Compiled.t(), any(), keyword()) ::
  {:ok, %{run_id: binary(), pid: pid(), ref: reference()}}

Spawn the run in a separate process and return a handle immediately.

Returns {:ok, %{run_id: binary, pid: pid, ref: reference}}. The caller can:

  • Process.demonitor(ref, [:flush]) if the result is no longer needed.
  • receive {:DOWN, ref, :process, pid, reason} to detect completion.
  • subscribe to [:raxol, :workflow, *] telemetry events filtered by run_id for progress updates.

The spawned process is not linked to the caller; a worker crash arrives as a :DOWN message rather than a synchronous exit signal.

async_resume(compiled, run_id, resume_value, opts \\ [])

@spec async_resume(Raxol.Workflow.Compiled.t(), binary(), any(), keyword()) ::
  {:ok, %{run_id: binary(), pid: pid(), ref: reference()}}
  | {:error, :no_saver_configured | :no_checkpoint, nil}

Spawn a resume in a separate process and return a handle immediately.

Mirrors async_invoke/3 but for the resume path. Looks up the latest checkpoint for run_id synchronously before spawning so the resume preconditions surface as a regular error tuple rather than hiding inside a normal-exit :DOWN message.

Returns {:ok, %{run_id, pid, ref}} on success or one of:

  • {:error, :no_saver_configured, nil} -- graph has no Saver
  • {:error, :no_checkpoint, nil} -- no checkpoint for run_id

resume_events(compiled, run_id, resume_value, opts \\ [])

@spec resume_events(Raxol.Workflow.Compiled.t(), binary(), any(), keyword()) ::
  Enumerable.t()

Resume a run and return a lazy Stream of CloudEvent structs.

Mirrors stream_events/3 but for the resume path. The stream carries telemetry events emitted during the resume invocation only; events emitted during the original (interrupted) run are not replayed.

Preconditions (no saver, no checkpoint) raise ArgumentError from the stream start function rather than yielding an empty stream that the consumer would block on. Callers that want a tuple-shaped preflight should use async_resume/4.

Accepts the same options as stream_events/3.

stream_events(compiled, initial_state, opts \\ [])

@spec stream_events(Raxol.Workflow.Compiled.t(), any(), keyword()) :: Enumerable.t()

Run the graph and return a lazy Stream of CloudEvent structs.

Each emitted telemetry event (run.started, node.started, node.completed, etc.) is converted to a Raxol.Core.Events.CloudEvent using the directive envelope and pushed onto the stream in the order the runtime emits it. The stream terminates after the first run-level terminal event (completed, interrupted, or failed).

Options

  • :timeout_ms - per-event receive timeout (default 60_000). The stream halts if no event arrives within the window.
  • :source - CloudEvent source URI override; defaults to Application.get_env(:raxol, :workflow_event_source, "raxol://workflow").

Any opts not consumed here are forwarded to Runtime.invoke/3.