Persistence contract for append-only run events.
Event retention is a backend policy. Since 0.1.2 lifecycle orchestration
appends events through Docket.Backend.TransitionStore; this focused write
remains temporarily for the 0.1.x compatibility adapter.
Summary
Callbacks
Appends retained events for run_id in sequence order.
Reads one retained event by its assigned positive sequence.
Reads the highest-sequence retained event for a run.
Reads a page of committed retained events for run_id.
Types
@type ctx() :: Docket.Backend.ctx()
@type scope() :: Docket.Backend.scope()
Callbacks
@callback append_events( ctx(), scope(), run_id :: String.t(), events :: [Docket.Event.t()] ) :: :ok | {:error, term()}
Appends retained events for run_id in sequence order.
The operation must be idempotent for an already-persisted {run_id, seq}
containing the same event. Conflicting content for that key is an error.
Every event must belong to run_id; a backend must reject a mismatched
event rather than storing it under the supplied run.
Event identities are assigned before this callback. The store never derives
a sequence from the maximum already-stored sequence, never substitutes the
run's checkpoint sequence, and accepts gaps left by persistence filtering. In particular,
:checkpoint_committed is an ordinary assigned event at this boundary.
For a non-empty append, scope is checked through the owning run and a
tenant mismatch is reported as {:error, :not_found}. An empty event list
validates the scope value but succeeds without a run lookup or storage
change.
@callback fetch_event( ctx(), scope(), run_id :: String.t(), seq :: pos_integer() ) :: {:ok, Docket.Event.t()} | {:error, :not_found} | {:error, term()}
Reads one retained event by its assigned positive sequence.
Ownership is enforced through the owning run. An unknown or out-of-scope
run and an absent or pruned sequence all return {:error, :not_found}.
A present corrupt row returns a typed
{:error, %Docket.Error{type: :corrupt_event_row}} and is never reported as
absent.
seq is trusted to be pre-validated by the caller as a positive integer.
@callback fetch_latest_event( ctx(), scope(), run_id :: String.t() ) :: {:ok, Docket.Event.t() | nil} | {:error, :not_found} | {:error, term()}
Reads the highest-sequence retained event for a run.
Ownership is enforced through the owning run. An unknown or out-of-scope
run returns {:error, :not_found}. A visible run with no retained events
returns {:ok, nil}, including when its complete history has been pruned.
A present corrupt latest row returns a typed
{:error, %Docket.Error{type: :corrupt_event_row}} and is never skipped in
favor of an older event.
@callback list_events( ctx(), scope(), run_id :: String.t(), opts :: %{after_seq: non_neg_integer(), limit: pos_integer()} ) :: {:ok, Docket.EventPage.t()} | {:error, :not_found} | {:error, term()}
Reads a page of committed retained events for run_id.
Events are returned in ascending sequence order, restricted to sequences
greater than opts.after_seq and limited to at most opts.limit rows. This
keyset scan skips pruned and persistence-filtered sequences, so a page and
the retention bounds are not promised contiguous.
Ownership is enforced through the owning run: a wrong tenant and an unknown
run both report {:error, :not_found}. The page rows and the returned
retention bounds are observed from one consistent snapshot. A corrupt or
undecodable stored row is a typed error and is never silently skipped: a
corrupt event row returns {:error, %Docket.Error{type: :corrupt_event_row}},
while a corrupt owning-run row propagates the same way as run reads, by
raising the typed error.
Options are trusted to be pre-validated by the caller: after_seq is a
non-negative integer and limit is a positive integer. A backend may assert
these rather than validate them.