Packages
extreme
1.0.2
1.1.4
1.1.3
1.1.2
1.1.1
1.1.1-rc01
1.1.0
1.1.0-rc9
1.1.0-rc8
1.1.0-rc7
1.1.0-rc6
1.1.0-rc5
1.1.0-rc4
1.1.0-rc3
1.1.0-rc2
1.1.0-rc1
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
Elixir TCP client for EventStore.
Current section
Files
Jump to
Current section
Files
lib/extreme/reading_subscription.ex
defmodule Extreme.ReadingSubscription do
use GenServer
require Logger
alias Extreme.RequestManager
alias Extreme.Messages, as: Msg
alias Extreme.SharedSubscription, as: Shared
defmodule State do
defstruct ~w(base_name correlation_id subscriber read_params buffered_messages read_until status)a
end
@doc """
Spawns Subscription for read_and_stay_subscribed
"""
def start_link(base_name, correlation_id, subscriber, read_params) do
GenServer.start_link(
__MODULE__,
{base_name, correlation_id, subscriber, read_params}
)
end
@impl true
def init(
{base_name, correlation_id, subscriber,
{stream, from_event_number, per_page, resolve_link_tos, require_master, ack_timeout}}
) do
read_params = %{
stream: stream,
from_event_number: from_event_number,
per_page: per_page,
resolve_link_tos: resolve_link_tos,
require_master: require_master,
ack_timeout: ack_timeout
}
state = %State{
base_name: base_name,
correlation_id: correlation_id,
subscriber: subscriber,
read_params: read_params,
status: :reading_events,
buffered_messages: [],
read_until: -1
}
{:ok, subscription_confirmation} = Shared.subscribe(state)
read_until = subscription_confirmation.last_event_number + 1
GenServer.cast(self(), :read_events)
{:ok, %State{state | read_until: read_until}}
end
@impl true
def handle_call(:unsubscribe, from, state) do
:ok = Shared.unsubscribe(from, state)
{:noreply, state}
end
@impl true
def handle_cast({:process_push, fun}, %{status: :subscribed} = state),
do: Shared.process_push(fun, state)
def handle_cast({:process_push, fun}, %{buffered_messages: buffered_messages} = state) do
case fun.() do
{_auth, _correlation_id, %Msg.StreamEventAppeared{} = e} ->
{:noreply, %{state | buffered_messages: [e.event | buffered_messages]}}
end
end
# We have read all planned events. Now lets push buffered messages.
def handle_cast(
:read_events,
%{read_params: %{from_event_number: from}, read_until: from} = state
) do
GenServer.cast(self(), :push_buffered_messages)
{:noreply, %{state | status: :pushing_buffered}}
end
def handle_cast(:read_events, state) do
{read_events_message, keep_reading} =
state.read_params
|> _read_events_message(state.read_until)
state =
if keep_reading,
do: state,
else: %{state | status: :pushing_buffered}
state.base_name
|> RequestManager.execute(read_events_message, Extreme.Tools.generate_uuid())
|> _process_read_response(state)
end
def handle_cast(:push_buffered_messages, state) do
state.buffered_messages
|> Enum.reverse()
|> Enum.each(&Shared.on_event(state.subscriber, &1, state.read_params.ack_timeout))
send(state.subscriber, :caught_up)
{:noreply, %{state | status: :subscribed, buffered_messages: []}}
end
defp _process_read_response({:error, :stream_deleted, _}, state) do
Logger.error(fn -> "Stream is hard deleted" end)
send(state.subscriber, {:extreme, :error, :stream_deleted, state.read_params.stream})
RequestManager._unregister_subscription(state.base_name, state.correlation_id)
{:stop, {:shutdown, :stream_deleted}, state}
end
defp _process_read_response({:error, :no_stream, _}, state) do
Logger.warning(fn -> "Stream doesn't exist yet" end)
{:extreme, :warn, :stream_soft_deleted, state.read_params.stream}
|> _caught_up(state)
end
defp _process_read_response({:ok, %Msg.ReadStreamEventsCompleted{} = response}, state) do
Logger.debug(fn -> "Last read event: #{inspect(response.next_event_number - 1)}" end)
response.events
|> Enum.each(&Shared.on_event(state.subscriber, &1, state.read_params.ack_timeout))
state =
response.next_event_number
|> _send_next_request(state)
{:noreply, state}
end
defp _caught_up(message, state) do
send(state.subscriber, message)
send(state.subscriber, :caught_up)
GenServer.cast(self(), :push_buffered_messages)
{:noreply, state}
end
defp _send_next_request(_, %{status: :pushing_buffered} = state) do
GenServer.cast(self(), :push_buffered_messages)
state
end
defp _send_next_request(next_event_number, state) do
Logger.debug(fn -> "Reading new batch of events #{inspect(state.read_params)}" end)
GenServer.cast(self(), :read_events)
%{state | read_params: %{state.read_params | from_event_number: next_event_number}}
end
defp _read_events_message(%{from_event_number: from, per_page: per_page} = params, read_until)
when from + per_page < read_until do
result =
Msg.ReadStreamEvents.new(
event_stream_id: params.stream,
from_event_number: from,
max_count: per_page,
resolve_link_tos: params.resolve_link_tos,
require_master: params.require_master
)
{result, true}
end
defp _read_events_message(params, read_until) do
result =
Msg.ReadStreamEvents.new(
event_stream_id: params.stream,
from_event_number: params.from_event_number,
max_count: read_until - params.from_event_number,
resolve_link_tos: params.resolve_link_tos,
require_master: params.require_master
)
{result, false}
end
end