Packages
ex_esdb
0.0.13-alpha
0.11.0
0.10.0
0.9.0
0.8.0
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14-alpha
0.0.13-alpha
0.0.12-alpha
0.0.11-alpha
0.0.10-alpha
0.0.9-alpha
0.0.8-alpha
0.0.6-alpha
0.0.5-alpha
0.0.4-alpha
0.0.3-alpha
0.0.2-alfa
0.0.1-alfa
ExESDB is a reincarnation of rabbitmq/khepri, specialized for use as a BEAM-native event store.
Current section
Files
Jump to
Current section
Files
lib/repl/subscriber.ex
defmodule ExESDB.Repl.Subscriber do
@moduledoc """
The Repl.Subscriber is a GenServer that:
- creates a permanent subscription to the store.
- subscribes to the events emitted by the store, by handling events directly.
"""
use GenServer
alias ExESDB.GatewayAPI, as: API
alias ExESDB.Options, as: Options
alias ExESDB.Themes, as: Themes
@impl true
def handle_info({:event_emitted, event}, state) do
%{
event_stream_id: stream_id,
event_type: event_type,
event_number: version,
data: payload
} = event
%{
store: store,
subscription_name: subscription_name
} = state
msg = "#{stream_id}:#{event_type} v(#{inspect(version)}) => #{inspect(payload, pretty: true)}"
IO.puts(Themes.subscription_received(subscription_name, msg))
store
|> API.ack_event(subscription_name, event, self())
{:noreply, state}
end
@impl true
def handle_info({:DOWN, _ref, :process, _pid, _reason}, state) do
{:stop, :normal, state}
end
@impl true
def handle_info(msg, state) do
Logger.warning("Unexpected message: #{inspect(msg)}")
{:noreply, state}
end
@spec start(
subscription_name :: String.t(),
stream :: String.t(),
start_from :: integer()
) :: pid()
def start(subscription_name, stream, start_from \\ 0) do
args = [
subscription_name: subscription_name,
start_from: start_from,
selector: stream
]
case start_link(args) do
{:ok, pid} ->
IO.puts("#{Themes.subscriber(pid)} for [#{inspect(subscription_name)}] is UP!")
pid
{:error, {:already_started, pid}} ->
IO.puts("#{Themes.subscriber(pid)} for [#{inspect(subscription_name)}] is UP!")
pid
{:error, reason} ->
raise "Failed to start subscriber. Reason: #{inspect(reason)}"
end
end
############## PLUMBING ##############
@impl true
def init(args) do
store = Options.store_id()
selector = selector(args)
subscription_name = subscription_name(args)
start_from = start_from(args)
store
|> API.save_subscription(
:by_stream,
selector,
subscription_name,
start_from,
self()
)
{:ok,
%{
store: store,
subscription_name: subscription_name,
selector: selector
}}
end
def start_link(args) do
GenServer.start_link(
__MODULE__,
args,
name: Module.concat(__MODULE__, subscription_name(args))
)
end
def child_spec(init_arg) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, [init_arg]},
type: :worker,
restart: :permanent,
shutdown: 5000
}
end
defp selector(args), do: Keyword.get(args, :selector, "$all")
defp subscription_name(args), do: Keyword.get(args, :subscription_name, "transient")
defp start_from(args), do: Keyword.get(args, :start_from, 0)
end