The delivery module StatifierRouter.Config names by default: one
binding's delivery of one event, as one transaction on the host's repo
(ADR-0003, section 1).
deliver/4 is the seam StatifierRouter.route/3 calls. It reads four
options of the configuration:
:store- the%StatifierPersistence.Storage{}executions are kept in. It must be built over the configuration's own:repo, so thatStatifierPersistence.Executions.create/4andStatifierPersistence.Executions.step/5write through the delivery's transaction rather than opening their own (statifier_persistence's README, "Writing inside a caller's transaction").:executor- theStatifierPersistence.Executorboth doors hand their effects to.:resolver- theStatifierRouter.Resolver, a module or an arity-2 fun, answering{content_hash, machine}or{:error, reason}for(scope, document): the chart a new execution ofdocumentstarts on (ADR-0002, section 4). It is called only when the delivery is about to create an execution.:chart_resolver-(content_hash) -> {:ok, machine}or:error: the compiled chart an existing execution started on, whichstep/5is handed. An existing execution keeps the chart it started on, so this is looked up by the content hash its record carries, never by its document.
What one delivery does
The transaction first claims (binding_id, message_id) with
StatifierRouter.Dedupe.claim/4, its first write under every create
mode. When the pair's row is present and unexpired, the delivery is a
duplicate, and the transaction writes the ledger row and nothing else:
no address row is read or written (ADR-0003, section 6). Past the claim,
the binding's create decides the rest (ADR-0003, section 4):
:if_absentreads the address row for(scope, document, key). When there is none, it mints an execution id and inserts the row with it, an insert that inserts nothing on a conflict with the unique index and does not fail the transaction; when it inserted nothing, another delivery's row won the race, and a following statement reads that row (ADR-0003, section 3). For the row it inserted, it asks the resolver for the chart and callscreate/4under the minted id; for a row it read, it reads the execution's status.:neverreads the address row and never inserts one. When there is none, the outcome is{:dropped, binding_id, :no_execution}: nothing is created or stepped, and the ledger row and the claim's dedupe row are the delivery's only writes. For a row it read, it reads the execution's status, as:if_absentdoes.:always_newneither reads nor writes an address row (ADR-0002, section 7). It mints an execution id, asks the resolver for the chart and callscreate/4under that id, for every delivery; the minted id is the only handle on the execution.
Then it calls step/5 with the event, unless the execution is
terminal, writes the ledger row (ADR-0004, section 4) and commits.
The outcomes are {:created_and_delivered, binding_id, execution_id}
when this delivery created the execution, {:delivered, binding_id, execution_id} when it stepped one that existed, {:duplicate, binding_id} when the claim found the pair already handled,
{:dropped, binding_id, :no_execution} for a :never binding whose
address has no row, and {:dropped, binding_id, :finished} when the
execution was terminal: read terminal before the step, created already
terminal, or answered {:discarded, execution} by step/5 (ADR-0004,
section 3). A terminal sighting through an address row stamps the row's
terminal_seen_at when it is still empty (ADR-0002, section 5); the
row itself is left in place, and removing it is
StatifierRouter.Addresses.reap/2's. Every outcome but the duplicate
commits the dedupe row the claim wrote, a drop included, and a
duplicate's ledger row carries the key and no execution id (ADR-0004,
section 4).
Nothing here writes the input log: step/5 appends the event it steps
(ADR-0003, section 1).
An {:error, reason} from create/4, step/5 or
StatifierPersistence.Storage.fetch_execution/2,
{:error, {:unresolved_document, document, reason}} when the resolver
answers {:error, reason}, and
{:error, {:chart_not_resolved, content_hash}} when the chart resolver
answers :error, roll the whole transaction back and are returned. A
raise rolls it back the same way and propagates; nothing here rescues
it. Effects the executor was handed before a rollback stay fired
(ADR-0003, section 2).
The execution id is a UXID with the prefix ex, minted by
UXID.generate!/1; nothing is derived from the address (ADR-0002,
section 3).
Summary
Functions
Delivers one event for one binding under key, as the module
documentation describes, and answers with the outcome or
{:error, reason}.
Functions
@spec deliver( StatifierRouter.Config.t(), StatifierRouter.Binding.t(), String.t(), StatifierRouter.delivery() ) :: StatifierRouter.outcome() | {:error, term()}
Delivers one event for one binding under key, as the module
documentation describes, and answers with the outcome or
{:error, reason}.