OTP-native durable execution engine for Elixir.
Continuum lets you write a multi-step business process as straight-line Elixir code. The process survives crashes, node restarts, and partitions: the engine journals each effect to Postgres and replays the workflow's history through the same orchestration code on resume.
See Continuum.Workflow for the workflow DSL and Continuum.Activity for
activities (the only place side effects are allowed inside a workflow).
Public API
children/1— Postgres runtime child specs for host supervision treesstart/3— start a new workflow runsignal/3— deliver an external signal to a running workflowcancel/2— cancel a running workflowawait/2— block until a workflow completes (test/synchronous use)query/1andget_run/2— inspect durable runsset_attributes/3— externally update run search attributesnow/0,uuid4/0,random/0,side_effect/1— deterministic primitives callable from workflow codepatched?/1— journaled patch marker for compatible workflow changes
Summary
Functions
Block until the run completes. Test/synchronous use only.
Cancel a running workflow.
Returns runtime child specs for a named, non-default Continuum instance.
Load one durable run by id.
Whether we are currently executing inside a workflow process. Useful in helper modules that branch on context.
The current wall-clock time, journaled and replayed deterministically.
Journaled patch marker for in-place, backward-compatible workflow changes.
Query durable runs with a closed, structured query spec.
Query durable runs for a named Continuum instance.
A pseudo-random float in [0, 1), journaled and replayed deterministically.
Merge JSON-encodable search attributes into a durable run row.
General-purpose escape hatch for an impure read whose result must be journaled and replayed.
Deliver a signal to a running workflow.
Deliver a signal to a running workflow, selecting a Continuum instance with
:instance.
Start a new workflow run.
The current UTC date, journaled and replayed deterministically.
Recover an activity's raw return value from a compensation handle.
A v4 UUID, journaled and replayed deterministically.
Types
Functions
Block until the run completes. Test/synchronous use only.
Cancel a running workflow.
@spec children(keyword()) :: [Supervisor.child_spec()]
Returns runtime child specs for a named, non-default Continuum instance.
children =
[
MyApp.Repo,
Continuum.children(name: :billing_continuum, repo: MyApp.Repo)
]The default Continuum instance is owned by Continuum.Application and
Continuum.children() returns [] to avoid duplicate process names.
Child-specific options may be passed with :workflow_modules,
:heartbeater, :run_supervisor,
:activity_supervisor, :recovery, :dispatcher, :activity_dispatcher,
:timer_wheel, :signal_router, and :snapshotter.
Passing false for a child omits it from the returned list.
Load one durable run by id.
@spec in_workflow?() :: boolean()
Whether we are currently executing inside a workflow process. Useful in helper modules that branch on context.
The current wall-clock time, journaled and replayed deterministically.
Journaled patch marker for in-place, backward-compatible workflow changes.
def run(input) do
if Continuum.patched?(:add_fraud_check_v2) do
activity FraudCheck.v2(input)
else
activity FraudCheck.v1(input)
end
endInside a workflow the first call to patched?(name) at a given source line
journals a patched event with value: true and returns true; the value
is then replayed on resume so the run never changes branch mid-flight. A run
that is replaying history recorded before the patch line existed returns
false without consuming any event, keeping in-flight runs on the old path.
Outside a workflow process (test setup, ordinary code) it returns false.
Like now/0 and uuid4/0, this is a macro so it captures __CALLER__ for a
stable command identity; modules that call it must require Continuum
(use Continuum.Workflow does this for you).
Query durable runs with a closed, structured query spec.
See Continuum.Query for supported :where, :order_by, and pagination
options. Querying requires a Postgres-backed Continuum instance.
@spec query( atom() | Continuum.Runtime.Instance.t(), keyword() ) :: {:ok, map()} | {:error, term()}
Query durable runs for a named Continuum instance.
A pseudo-random float in [0, 1), journaled and replayed deterministically.
Merge JSON-encodable search attributes into a durable run row.
This is external metadata. It is not journaled and workflow code cannot read it during replay.
General-purpose escape hatch for an impure read whose result must be journaled and replayed.
The function is called once on first execution; its return value is
journaled and returned on every subsequent replay. Return values must be
serializable via :erlang.term_to_binary/1 — pids, refs, ports, and
similar local-only terms are rejected.
Deliver a signal to a running workflow.
Deliver a signal to a running workflow, selecting a Continuum instance with
:instance.
@spec start(workflow_module(), input(), keyword()) :: {:ok, run_id()} | {:error, term()}
Start a new workflow run.
Options include :instance for selecting a named Continuum instance,
:namespace for soft tenant scoping of list/query paths, :trace_context for
persisting an opaque W3C traceparent binary that
observability integrations can use to link resumed run attempts, and
:attributes for JSON-encodable search metadata stored on the run row.
The current UTC date, journaled and replayed deterministically.
Recover an activity's raw return value from a compensation handle.
When an activity/2 call uses compensate:, a success is returned as
{:ok, %Continuum.ActivityRef{}} rather than a bare term. unwrap/1 peels the
ref back to the activity's raw return:
unwrap(%Continuum.ActivityRef{raw_result: raw})→rawunwrap({:ok, %Continuum.ActivityRef{} = ref})→ref.raw_resultunwrap(other)→other(activities withoutcompensate:are unchanged)
A v4 UUID, journaled and replayed deterministically.