Packages
eventstore
0.2.0
1.4.8
1.4.7
1.4.6
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.3
1.2.2
1.2.1
1.2.0
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc.0
0.17.0
0.16.2
0.16.1
0.16.0
0.15.1
0.15.0
0.14.0
0.14.0-rc.0
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.11.0-rc.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.1
0.2.0
0.1.0
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
Event store using PostgreSQL for persistence.
Current section
Files
Jump to
Current section
Files
lib/event_store/subscriptions/persistent_subscription.ex
defmodule EventStore.Subscriptions.PersistentSubscription do
require Logger
defmodule SubscriptionData do
defstruct stream_uuid: nil,
subscription_name: nil,
subscriber: nil,
last_seen_event_id: 0,
latest_event_id: 0
end
alias EventStore.Storage
alias EventStore.Subscriptions.PersistentSubscription
use Fsm, initial_state: :initial, initial_data: %SubscriptionData{}
@all_stream "$all"
defstate initial do
defevent subscribe(stream_uuid, subscription_name, subscriber), data: %SubscriptionData{} = data do
case subscribe_to_stream(stream_uuid, subscription_name) do
{:ok, subscription} ->
data = %SubscriptionData{data |
stream_uuid: stream_uuid,
subscription_name: subscription_name,
subscriber: subscriber,
last_seen_event_id: subscription.last_seen_event_id
}
next_state(:catching_up, data)
{:error, reason} ->
next_state(:failed, data)
end
end
end
defstate catching_up do
defevent catch_up, data: %SubscriptionData{last_seen_event_id: last_seen_event_id} = data do
case query_latest_event_id do
0 ->
# no published events
next_state(:subscribed, data)
^last_seen_event_id ->
# already seen latest published event
next_state(:subscribed, data)
latest_event_id ->
# must catch-up with all unseen events
data = catch_up_to_event(data, latest_event_id)
next_state(:subscribed, data)
end
end
# ignore event notifications while catching up; but remember the latest event id
defevent notify_events(events, latest_event_id), data: %SubscriptionData{} = data do
data = %SubscriptionData{data | latest_event_id: latest_event_id}
next_state(:catching_up, data)
end
end
defstate subscribed do
defevent notify_events(events), data: %SubscriptionData{} = data do
# TODO: move to catching-up state if the event id of the first event is not `data.last_seen_event_id + 1`
latest_event_id = List.last(events).event_id
notify_subscriber(data, events)
ack_events(data, events)
data = %SubscriptionData{data |
latest_event_id: latest_event_id,
last_seen_event_id: latest_event_id
}
next_state(:subscribed, data)
end
defevent unsubscribe, data: %SubscriptionData{} = data do
next_state(:unsubscribed, data)
end
end
defstate unsubscribed do
end
defp subscribe_to_stream(stream_uuid, subscription_name) do
Storage.subscribe_to_stream(stream_uuid, subscription_name)
end
defp query_latest_event_id do
{:ok, latest_event_id} = Storage.latest_event_id
latest_event_id
end
defp catch_up_to_event(%SubscriptionData{stream_uuid: stream_uuid, last_seen_event_id: last_seen_event_id} = data, latest_event_id) do
case unseen_events(data) do
{:ok, events} ->
# chunk events by stream
events
|> Enum.chunk_by(fn event -> event.stream_id end)
|> Enum.each(fn events_by_stream ->
notify_subscriber(data, events_by_stream)
ack_events(data, events_by_stream)
end)
end
data = %SubscriptionData{data |
latest_event_id: latest_event_id,
last_seen_event_id: latest_event_id
}
end
defp unseen_events(%SubscriptionData{stream_uuid: @all_stream, last_seen_event_id: last_seen_event_id}) do
start_event_id = last_seen_event_id + 1
Storage.read_all_streams_forward(start_event_id)
end
defp unseen_events(%SubscriptionData{stream_uuid: stream_uuid, last_seen_event_id: last_seen_event_id}) do
start_version = last_seen_event_id + 1
Storage.read_stream_forward(stream_uuid, start_version)
end
defp notify_subscriber(%SubscriptionData{subscriber: subscriber} = data, events) do
send(subscriber, {:events, events})
end
defp ack_events(%SubscriptionData{subscription_name: subscription_name} = data, events) do
# TODO
# latest_event_id = List.last(events).event_id
#
# Storage.ack_last_seen_event(stream_id, subscription_name, latest_event_id)
end
end