Packages
ex_esdb
0.0.12-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/ex_esdb/gateway_api.ex
defmodule ExESDB.GatewayAPI do
@moduledoc """
Though the GatewayAPI GenServer is started on each node in the cluster,
it acts as a simple high-availability proxy and load balancer for the
GatewayWorker processes in the cluster.
"""
@type store :: atom()
@type stream :: String.t()
@type subscription_name :: String.t()
@type error :: term
@type subscription_type :: :by_stream | :by_event_type | :by_event_pattern | :by_event_payload
@type selector_type :: String.t() | map()
use GenServer
require Logger
alias ExESDB.Themes, as: Themes
@doc """
Gets a list of all gateway worker pids.
"""
@spec gateway_worker_pids() :: list()
def gateway_worker_pids,
do:
Swarm.registered()
|> Enum.filter(fn {name, _} -> match?({:gateway_worker, _, _}, name) end)
|> Enum.map(fn {_, pid} -> pid end)
@doc """
Gets a random pid of a gateway worker in the cluster.
"""
@spec random_gateway_worker() :: pid()
def random_gateway_worker,
do:
gateway_worker_pids()
|> Enum.random()
@doc """
Get the version of a stream.
"""
@spec get_version(
store :: atom(),
stream :: stream
) ::
{:ok, integer} | {:error, term}
def get_version(store, stream),
do:
GenServer.call(
random_gateway_worker(),
{:get_version, store, stream}
)
@doc """
Get the subscriptions for a store.
"""
@spec get_subscriptions(store :: atom()) :: {:ok, list()} | {:error, term}
def get_subscriptions(store),
do:
GenServer.call(
random_gateway_worker(),
{:get_subscriptions, store}
)
@doc """
Acknowledge receipt of an event by a subscriber to persistent subscription.
"""
@spec ack_event(
store :: atom(),
subscription_name :: String.t(),
subscriber_pid :: pid(),
event :: map()
) :: :ok | {:error, term}
def ack_event(store, subscription_name, subscriber_pid, event),
do:
GenServer.cast(
random_gateway_worker(),
{:ack_event, store, subscription_name, subscriber_pid, event}
)
@doc """
Append events to a stream.
## Parameters
- store: the id of the store
- stream_id: the id of the stream
- events: the events to append
## Returns
{:ok, new_version} where new_version is the new version of the stream
{:error, reason} if there was an error
"""
@spec append_events(
store :: atom(),
stream_id :: stream,
events :: list()
) :: {:ok, integer} | {:error, term}
def append_events(store, stream_id, events),
do:
GenServer.call(
random_gateway_worker(),
{:append_events, store, stream_id, events}
)
@doc """
Get events from a stream, staring from a given version, in a given direction.
"""
@spec get_events(
store :: atom(),
stream_id :: stream,
start_version :: integer,
count :: integer,
direction :: :forward | :backward
) :: {:ok, list()} | {:error, term}
def get_events(store, stream_id, start_version, count, direction \\ :forward),
do:
GenServer.call(
random_gateway_worker(),
{:get_events, store, stream_id, start_version, count, direction}
)
@doc """
Get all streams from the store.
## Parameters
- store: the id of the store
## Returns
- a list of all streams in the store
"""
@spec get_streams(store :: atom()) :: {:ok, list()} | {:error, term()}
def get_streams(store),
do:
GenServer.call(
random_gateway_worker(),
{:get_streams, store}
)
@doc """
Add a permanent or transient subscription.
"""
@spec save_subscription(
store :: atom(),
type :: atom(),
selector :: String.t() | map(),
subscription_name :: String.t(),
start_from :: non_neg_integer(),
subscriber :: pid() | nil
) :: :ok
def save_subscription(
store,
type,
selector,
subscription_name \\ "transient",
start_from \\ 0,
subscriber \\ nil
) do
GenServer.cast(
random_gateway_worker(),
{:save_subscription, store, type, selector, subscription_name, start_from, subscriber}
)
:ok
end
@doc """
Remove a permanent or transient subscription.
"""
@spec remove_subscription(
store :: any,
type :: subscription_type,
selector :: selector_type,
subscription_name :: subscription_name
) :: :ok | {:error, error}
def remove_subscription(store, type, selector, subscription_name \\ "transient"),
do:
GenServer.cast(
random_gateway_worker(),
{:remove_subscription, store, type, selector, subscription_name}
)
################## PLUMBING ##################
def init(opts) do
IO.puts("#{Themes.gateway_api(self())} is UP!")
{:ok, opts}
end
def child_spec(opts),
do: %{
id: __MODULE__,
start: {__MODULE__, :start_link, [opts]},
type: :worker,
restart: :permanent,
shutdown: 5000
}
def start_link(opts),
do:
GenServer.start_link(
__MODULE__,
opts,
name: __MODULE__
)
end