AshArcadic.ReplicantSink (AshArcadic v0.2.0)

Copy Markdown View Source

use AshArcadic.ReplicantSink, domains: [...], checkpoint_resource: MyGraph.Checkpoint, slot_name: "sirtify_graph" generates a Replicant.Sink implementation bound to a host's config — the Postgres→ArcadeDB effect-once CDC mirror's delivery seam.

The generated module implements (../replicant/lib/replicant/sink.ex — the behaviour):

  • checkpoint/0{:ok, checkpoint_resource.for_slot(slot_name)} — the durable integer watermark, or nil (never applied). Reads the baked checkpoint resource directly (no resolver index), so it is cheap and safe to call at pipeline start for the Ch3 start-mode decision.
  • handle_transaction/1 → delegates to AshArcadic.Replicant.Apply.apply_transaction/2 (the effect-once core: one Ash.transaction co-committing the mirrored writes and the watermark advance).
  • sink_kind/0:state_mirror. The graph is a rebuildable projection; this gates Replicant.Config's go-forward start guard (an empty-checkpoint :state_mirror is refused without go_forward_only: true OR a snapshot — see the Pipeline).
  • handle_snapshot/2 + handle_snapshot_complete/1 → the Ch3 bootstrap (both required, so Replicant.Sink.supports_snapshot?/1 is true and snapshot: true is accepted; a partial impl is rejected :snapshot_unsupported). Delegated to AshArcadic.Replicant.Sink.Impl — see there for the first_for_table? redo-safety semantics.

Config (__config__/0)

The Replicant.Sink callbacks carry no pipeline context, so the config is baked into the generated module: domains, checkpoint_resource, slot_name. The AshArcadic.Replicant.Apply.config map (resolver_index + checkpoint + slot + authorize?: false) is assembled by AshArcadic.Replicant.Sink.Impl.config/1, which builds the resolver index ONCE (cached in :persistent_term keyed by slot) and fails closed at build time on a duplicate/missing source_table — a mis-mapped mirror must not silently start.

optional: true compile note

replicant is an optional: true dep. AshArcadic.Replicant.Apply / Sink.Impl / Pipeline reference %Replicant.Transaction{} / %Replicant.Change{} structs and Replicant.start_link/1 (hard compile-time dependencies), so those three modules are compile-gated on if Code.ensure_loaded?(Replicant.Sink) — a non-CDC host builds ash_arcadic without replicant and that subtree simply compiles away. A host that uses the CDC sink adds {:replicant, "~> 0.3"} to its own deps; use AshArcadic.ReplicantSink requires it (its @behaviour Replicant.Sink / delegation to Apply bind at the host's compile). If a host adds replicant AFTER an initial non-CDC compile, run mix deps.get && mix deps.clean ash_arcadic --build && mix compile so the gated subtree recompiles with replicant present (mix deps.compile ash_arcadic --force alone can rebuild ash_arcadic before replicant is compiled, leaving the gate false).