EventStoreDB adapter via Spear gRPC client.
Requires Spear.Connection in the supervision tree.
Configuration
config :orkestra, Orkestra.EventStore.EventStoreDB,
connection_string: "esdb://localhost:2113?tls=false"
Summary
Functions
Subscribes subscriber to receive events from stream_id_or_all starting
after from_position (exclusive).
Cancels the subscription identified by handle (the relay pid returned by
subscribe_from_position/3) and stops delivery.
Functions
@spec subscribe_from_position( Orkestra.EventStore.stream_id() | :all, integer(), pid() ) :: {:ok, pid()} | {:error, term()}
Subscribes subscriber to receive events from stream_id_or_all starting
after from_position (exclusive).
The from_position is translated by map_subscribe_from/2 (see there for the
full mapping and rationale):
-1/nil→:start(replay from the beginning, exactly once).:all+ a non-negative commit_position → a%Spear.Filter.Checkpoint{}(a raw integer cannot be used for:all— Spear would raiseFunctionClauseErrorinmap_all_position/1).- a named stream + a non-negative revision → the integer revision.
All non-:start forms are exclusive in Spear: from: N delivers only
events strictly after N, matching the D-01 monotonic contract, InMemory's
semantics, and the exactly-once projector — resuming from a saved checkpoint
never re-delivers the event at that checkpoint, so no manual skip is required.
Delivery shape (parity with InMemory)
Delivery does not hand subscriber the raw %Spear.Event{} structs that
Spear.subscribe/4 pushes. Instead a
Orkestra.EventStore.EventStoreDB.SubscriptionRelay process sits between
Spear and subscriber and delivers stored_event_with_position() maps —
%{id, type, data, metadata, stream_revision, global_position}, the same
shape produced by load_events/1,2 and the same shape delivered by
Orkestra.EventStore.InMemory. That is exactly what
Orkestra.Projector.GenServer.handle_info/2 pattern-matches on
(%{global_position: _}); handed raw Spear structs it would match nothing.
The relay also drops Spear's control messages (%Spear.Filter.Checkpoint{},
{:caught_up, _}, {:fell_behind, _}) which the projector has no clause for,
and applies the link-de-duplication subscription options (see the relay
moduledoc).
The returned handle is the relay pid (an opaque subscription handle, not a
Spear reference). Pass it to unsubscribe/1 to tear the subscription down;
it is also torn down automatically when subscriber dies.
Returns {:ok, subscription_handle} on success or {:error, reason} on
failure.
@spec unsubscribe(pid()) :: :ok
Cancels the subscription identified by handle (the relay pid returned by
subscribe_from_position/3) and stops delivery.
Idempotent: returns :ok whether or not the relay is still alive. The
Orkestra.Projector.GenServer calls this on rebuild (to resubscribe from a
reset checkpoint) and it is a no-op-safe cleanup path; on normal projector
termination the relay tears itself down via its subscriber monitor, so an
explicit call is not required there.