evoq_store_subscription (evoq v1.23.3)

View Source

Bridge between event stores and evoq routing infrastructure.

Creates a single $all subscription to a reckon-db store, receiving ALL events in global store order. Events are filtered locally by checking against registered event types in the type registry.

This preserves causal ordering across event types within a store, which is critical when projections for related events must execute in the order they were appended (e.g., license_initiated before license_published).

How It Works

1. Replays all historical events from the store (catch-up phase) 2. Subscribes to the store's $all stream (by_stream, selector $all) 3. Receives ALL new events in store-global order 4. For each event, checks if any handler is registered for its type 5. Routes matching events to evoq_event_router and evoq_pm_router 6. Skips events with no registered handlers (zero cost)

Usage

Start one instance per event store:

  %% In your application supervisor or startup:
  evoq_store_subscription:start_link(plugins_store)
  evoq_store_subscription:start_link(settings_store, #{})

All modules implementing evoq behaviours that have registered with evoq_event_type_registry will automatically receive matching events.

Summary

Functions

Keep only the events matching EventType, in order. Exported for testing (pure, no store needed) -- backfill_loop/5 is the only real caller, and needs a live store to exercise past this point.

Start a store subscription with default options.

Start a store subscription with options.

Types

evoq_event/0

-type evoq_event() ::
          #evoq_event{event_id :: binary(),
                      event_type :: binary() | undefined,
                      stream_id :: binary(),
                      version :: non_neg_integer(),
                      data :: map() | binary(),
                      metadata :: map(),
                      tags :: [binary()] | undefined,
                      timestamp :: integer(),
                      epoch_us :: integer(),
                      data_content_type :: binary(),
                      metadata_content_type :: binary(),
                      prev_event_hash :: binary() | undefined}.

Functions

filter_by_type(Events, EventType)

-spec filter_by_type([evoq_event() | term()], binary()) -> [evoq_event() | term()].

Keep only the events matching EventType, in order. Exported for testing (pure, no store needed) -- backfill_loop/5 is the only real caller, and needs a live store to exercise past this point.

start_link(StoreId)

-spec start_link(atom()) -> {ok, pid()} | {error, term()}.

Start a store subscription with default options.

start_link(StoreId, Opts)

-spec start_link(atom(), map()) -> {ok, pid()} | {error, term()}.

Start a store subscription with options.

No options are currently read from Opts -- catch-up always starts at 0 and the live $all subscription's own starting point is always whatever catch_up_historical/2 actually scanned up to (see handle_continue/2), not a caller-supplied position. Opts is still accepted and threaded through (subscribe_to_all/3 receives it) for a future option this shape doesn't need yet -- e.g. pool_size.