Postgres-backed read-model target.
Builds an Ecto.Multi from the batch's handler results, appends one
per-stream scriba_positions upsert via Scriba.Position.multi/5 per
stream represented in the batch, and runs everything in a single
Repo.transaction/1. Read-model writes and per-stream cursor advances
commit atomically together.
Handler return contract (§4.2)
:skip— no Multi op for this event (its stream's cursor still advances if the event was not dedup-skipped; see Pipelinestream_advances).{:insert, schema_struct}—Ecto.Multi.insert/3.{:update, schema_module, filter_keyword, [set: keyword]}—Ecto.Multi.update_all/4filtered byfilter_keyword.{:delete, schema_module, filter_keyword}—Ecto.Multi.delete_all/3.{:multi, %Ecto.Multi{}}— merged into the batch's Multi.
Failure-shape results ({:error, reason} and the internal
{:exception, exception, stacktrace} tag produced when a handler raises)
do NOT reach apply_handler_result/3. Pipeline partitions them out and
hands them to apply_batch/6 as the dead_letters list; this module
appends a Scriba.DeadLetter.multi/4 step per dead-letter to the same
Ecto.Multi that carries read-model writes and cursor advances. Result:
one atomic transaction commits success rows, dead-letter rows, and
advanced cursors together.
Usage
target: {Scriba.Target.Ecto, repo: MyApp.Repo}
Summary
Functions
Assembles the Ecto.Multi for a batch without running the transaction.
Exposed so callers can inspect the structure of the assembled Multi.
Functions
@spec build_multi( [Scriba.Event.t()], [term()], %{name: String.t(), version: pos_integer()}, %{required(String.t()) => non_neg_integer()}, [{Scriba.Event.t(), term()}] ) :: Ecto.Multi.t()
Assembles the Ecto.Multi for a batch without running the transaction.
Exposed so callers can inspect the structure of the assembled Multi.
stream_advances is %{stream_id => max_position} for each stream the
batch touches. One position-update step is appended per stream, keyed
{:scriba_position, stream_id}.
dead_letters is a list of {event, error} tuples for events whose
handler returned {:error, _} or raised. One dead-letter step is
appended per failed event, keyed {:scriba_dead_letter, event.id} (the
same key shape Scriba.DeadLetter.multi/4 produces).
Order of steps in the assembled Multi:
- Read-model ops (one
{:scriba_event, event.id}per success event). - Per-stream cursor advances (one
{:scriba_position, stream_id}). - Dead-letter inserts (one
{:scriba_dead_letter, event.id}).