Atomically enqueue workflows alongside application changes in Ecto.Multi.
The transaction's repo must be the selected Continuum instance's repo. Enqueueing only inserts durable work: the dispatcher acquires a lease and starts an engine after commit. No engine or activity starts in the caller's transaction, and no after-commit callback is needed for recovery.
Summary
Functions
Adds a workflow enqueue operation to a Multi.
Functions
@spec enqueue( Ecto.Multi.t(), Ecto.Multi.name(), module(), term(), keyword() | function() ) :: Ecto.Multi.t()
Adds a workflow enqueue operation to a Multi.
input and opts may each be a value or a one-argument function of prior
Multi changes. Options are :instance, :run_id, :idempotency_key,
:namespace, :attributes, and :trace_context.
Ecto.Multi.new()
|> Ecto.Multi.insert(:order, changeset)
|> Continuum.Multi.enqueue(:workflow, OrderFlow,
fn %{order: order} -> %{order_id: order.id} end,
fn %{order: order} -> [idempotency_key: "order:#{order.id}"] end)
|> Repo.transaction()The named change is %{run_id: id, status: :enqueued | :existing}. Reusing
an idempotency key returns the original root ID, including after pruning;
it does not replace its input or metadata. Other Multi operations still run
on a duplicate, so give business writes their own uniqueness constraints.
Any later Multi failure rolls back both the run and its ingress key. An unleased row is intentional here: only the dispatcher may acquire its first lease after commit. Keep a dispatcher enabled for this repo.