Validates Replicant.start_link/1 options and enforces the go-forward-only
start guard: a :state_mirror sink resuming from an empty
checkpoint without go_forward_only: true would silently deliver partial data
from the slot's creation point, so it is refused at start.
Pure — no processes, no connection. Replicant.start_link/1 (the facade) calls
validate/1 then guard/1 before spawning a pipeline.
Summary
Functions
The go-forward-only start guard. Refuses ONLY the exact unsafe triple — a
:state_mirror sink (default kind) with a definitively empty checkpoint
({:ok, nil}) and go_forward_only: false. An :append_log sink, a non-nil
checkpoint, go_forward_only: true, OR a checkpoint READ fault (raise/exit/
{:error, _}) all pass — the read-fault path is deliberately fail-open (a
re-dispatched already-persisted txn is deduped by the idempotent sink; only a
definitive empty checkpoint proves partial-delivery risk).
snapshot: true ALSO bypasses the empty-checkpoint refusal (alongside
go_forward_only: true): the backfill IS the safe seed, so an empty checkpoint
is the expected first-run state, not a partial-delivery risk.
Validate raw start_link options into a normalised config map, or return a
plain-atom error: :config_invalid (missing/mis-shaped connection or opts),
:invalid_identifier (slot/publication fails the Postgres-identifier
allowlist), :invalid_sink (sink is not a module exporting the two mandatory
callbacks), :conflicting_start_mode (go_forward_only: true AND
snapshot: true — mutually exclusive start intents), :snapshot_unsupported
(snapshot: true but the sink is missing one/both snapshot callbacks),
:batch_unsupported (batch_delivery is set but the sink does not implement
handle_batch/1).
Types
@type t() :: %{ optional(:batch) => keyword() | nil, optional(:batch_delivery) => keyword() | nil, optional(:streaming) => keyword() | nil, connection: keyword(), slot_name: String.t(), publication: String.t(), sink: module(), go_forward_only: boolean(), snapshot: boolean(), max_inflight_lag: pos_integer(), checkpoint_store: keyword() | nil }
Functions
@spec guard(t()) :: :ok | {:error, :go_forward_required}
The go-forward-only start guard. Refuses ONLY the exact unsafe triple — a
:state_mirror sink (default kind) with a definitively empty checkpoint
({:ok, nil}) and go_forward_only: false. An :append_log sink, a non-nil
checkpoint, go_forward_only: true, OR a checkpoint READ fault (raise/exit/
{:error, _}) all pass — the read-fault path is deliberately fail-open (a
re-dispatched already-persisted txn is deduped by the idempotent sink; only a
definitive empty checkpoint proves partial-delivery risk).
snapshot: true ALSO bypasses the empty-checkpoint refusal (alongside
go_forward_only: true): the backfill IS the safe seed, so an empty checkpoint
is the expected first-run state, not a partial-delivery risk.
@spec validate(keyword()) :: {:ok, t()} | {:error, :config_invalid | :invalid_identifier | :invalid_sink | :conflicting_start_mode | :snapshot_unsupported | :batch_unsupported}
Validate raw start_link options into a normalised config map, or return a
plain-atom error: :config_invalid (missing/mis-shaped connection or opts),
:invalid_identifier (slot/publication fails the Postgres-identifier
allowlist), :invalid_sink (sink is not a module exporting the two mandatory
callbacks), :conflicting_start_mode (go_forward_only: true AND
snapshot: true — mutually exclusive start intents), :snapshot_unsupported
(snapshot: true but the sink is missing one/both snapshot callbacks),
:batch_unsupported (batch_delivery is set but the sink does not implement
handle_batch/1).