A per-subscription relay process that adapts Spear's raw subscription delivery
to the Orkestra.EventStore subscriber contract.
Spear.subscribe/4 pushes Spear.Event.t/0 structs (plus
Spear.Filter.Checkpoint.t/0, {:caught_up, ref}, {:fell_behind, ref}
and {:eos, ref, reason} control messages) directly to the subscribing
process. The Orkestra projector, however, is written against a single
delivery contract shared with Orkestra.EventStore.InMemory: it receives
only Orkestra.EventStore.stored_event_with_position/0 maps (maps
carrying a :global_position key) and has no handle_info/2 clause for
Spear's control structs — an unmatched %Spear.Filter.Checkpoint{} would
crash it.
This relay closes that gap. It becomes the Spear subscriber, and for each message it receives it:
- transforms a
%Spear.Event{}into astored_event_with_position()map (viaOrkestra.EventStore.EventStoreDB.to_stored_event/1, the same mapping used byload_events/1,2) and forwards it to the real subscriber — so the projector sees exactly the shape InMemory delivers; - silently drops every Spear control message (checkpoints, caught_up, fell_behind) — they carry no domain event;
- stops (cancelling the underlying Spear subscription) when it receives an
{:eos, _, reason}end-of-stream, or when the real subscriber dies (itProcess.monitor/1s the subscriber), so no subscription is leaked.
Subscription options (link-event de-duplication)
For an :all subscription the relay passes resolve_links?: false and
filter: Spear.Filter.exclude_system_events() to Spear.subscribe/4. With
EventStoreDB standard projections enabled, an event is also surfaced through
system link streams ($ce-*, $et-*) that share the original event's
commit_position; resolve_links?: true (Spear's default) would therefore
deliver the same event several times, inflating checkpoints and causing
re-processing. Disabling link resolution and excluding $-prefixed system
streams delivers each event exactly once.
Note that orkestra's own snapshot-<stream> streams do not start with
$, so their events still reach an :all subscriber. That is intentional:
the projector's handler is expected to ignore event types it does not
recognise (advancing the checkpoint without dead-lettering them).
Summary
Functions
Starts a relay linking a Spear subscription on connection to subscriber.
Cancels the relay's Spear subscription and stops the relay process.
Functions
@spec start( Spear.Connection.t(), Orkestra.EventStore.stream_id() | :all, term(), pid() ) :: {:ok, pid()} | {:error, term()}
Starts a relay linking a Spear subscription on connection to subscriber.
Returns {:ok, relay_pid} once the Spear subscription is confirmed, or
{:error, reason} if Spear.subscribe/4 fails. The returned pid is the
opaque subscription handle: pass it to
Orkestra.EventStore.EventStoreDB.unsubscribe/1 to tear the subscription
down (this is also done automatically when subscriber dies).
@spec stop(pid()) :: :ok
Cancels the relay's Spear subscription and stops the relay process.
Idempotent and safe to call with a dead pid.