Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 7 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,15 @@ jobs:
name: Build and test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- elixir: 1.17.x
- elixir: 1.20.x
otp: 29
- elixir: 1.19.x
otp: 28
- elixir: 1.18.x
otp: 27
- elixir: 1.16.x
otp: 26
- elixir: 1.15.x
otp: 26
- elixir: 1.14.x
otp: 26
check_formatted: "ignore"
- elixir: 1.13.x
otp: 25
check_formatted: "ignore"

services:
postgres:
Expand Down Expand Up @@ -61,14 +56,14 @@ jobs:
run: mix deps.get

- name: Check formatting
if: ${{ matrix.check_formatted != 'ignore' }}
run: mix format --check-formatted

- name: Setup EventStore test databases
run: |
MIX_ENV=test mix event_store.setup
MIX_ENV=jsonb mix event_store.setup
MIX_ENV=text_ids mix event_store.setup
MIX_ENV=migration mix event_store.setup

- name: Compile
run: mix compile --warnings-as-errors
Expand Down
2 changes: 0 additions & 2 deletions .tool-versions

This file was deleted.

2 changes: 1 addition & 1 deletion config/bench.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Config

# no logging for benchmarking
config :logger, backends: []
config :logger, :default_handler, false

config :ex_unit,
assert_receive_timeout: 2_000,
Expand Down
2 changes: 1 addition & 1 deletion config/jsonb.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Config

config :logger, backends: []
config :logger, :default_handler, false

config :ex_unit,
capture_log: true,
Expand Down
2 changes: 1 addition & 1 deletion config/migration.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Config

config :logger, backends: []
config :logger, :default_handler, false

config :ex_unit,
capture_log: true,
Expand Down
2 changes: 1 addition & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Config

config :logger, backends: []
config :logger, :default_handler, false

config :ex_unit,
capture_log: [level: :warning],
Expand Down
2 changes: 1 addition & 1 deletion config/text_ids.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Config

config :logger, backends: []
config :logger, :default_handler, false

config :ex_unit,
capture_log: true,
Expand Down
175 changes: 175 additions & 0 deletions lib/event_store/fsm.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
defmodule EventStore.Fsm do
@moduledoc false

# Vendored from the `fsm` package (v0.3.1, MIT, Copyright (c) 2013 Saša Jurić)
# because upstream is unmaintained and its generated code does not compile
# cleanly under the Elixir type checker.

defmacro __using__(opts) do
quote do
import EventStore.Fsm

defstruct state: unquote(opts[:initial_state]),
data: unquote(opts[:initial_data])

@declaring_state nil
@declared_events MapSet.new()

def new(params \\ []), do: struct!(__MODULE__, params)

def state(%__MODULE__{state: state}), do: state
def data(%__MODULE__{data: data}), do: data

defp change_state(%__MODULE__{} = fsm, {:action_responses, responses}),
do: parse_action_responses(fsm, responses)

defp parse_action_responses(%__MODULE__{} = fsm, responses) do
Enum.reduce(responses, fsm, fn response, fsm ->
handle_action_response(fsm, response)
end)
end

defp handle_action_response(%__MODULE__{} = fsm, {:next_state, next_state}) do
%__MODULE__{fsm | state: next_state}
end

defp handle_action_response(%__MODULE__{} = fsm, {:new_data, new_data}) do
%__MODULE__{fsm | data: new_data}
end

defp handle_action_response(%__MODULE__{} = fsm, {:respond, response}) do
{response, fsm}
end
end
end

def next_state(state), do: {:action_responses, [next_state: state]}
def next_state(state, data), do: {:action_responses, [next_state: state, new_data: data]}

def respond(response), do: {:action_responses, [respond: response]}
def respond(response, state), do: {:action_responses, [next_state: state, respond: response]}

def respond(response, state, data),
do: {:action_responses, [next_state: state, new_data: data, respond: response]}

defmacro defstate(state, state_def) do
quote do
state_name =
case unquote(Macro.escape(state, unquote: true)) do
name when is_atom(name) -> name
{name, _, _} -> name
end

@declaring_state state_name
unquote(state_def)
@declaring_state nil
end
end

defmacro defevent(event, opts) do
do_defevent(event, opts, opts[:do])
end

defmacro defevent(event, opts, do: event_def) do
do_defevent(event, opts, event_def)
end

defmacro defeventp(event, opts) do
do_defevent(event, [{:private, true} | opts], opts[:do])
end

defmacro defeventp(event, opts, do: event_def) do
do_defevent(event, [{:private, true} | opts], event_def)
end

defp do_defevent(event_decl, opts, event_def) do
quote do
unquote(extract_args(event_decl, opts, event_def))
unquote(define_interface())
unquote(implement_transition())
end
end

defp extract_args(event_decl, opts, event_def) do
quote do
{event_name, args} =
case unquote(Macro.escape(event_decl, unquote: true)) do
:_ -> {:_, []}
name when is_atom(name) -> {name, []}
{name, _, args} -> {name, args || []}
end

private = unquote(opts[:private])
state_arg = unquote(Macro.escape(opts[:state] || quote(do: _), unquote: true))
data_arg = unquote(Macro.escape(opts[:data] || quote(do: _), unquote: true))
event_arg = unquote(Macro.escape(opts[:event] || quote(do: _), unquote: true))
args_arg = unquote(Macro.escape(opts[:args] || quote(do: _), unquote: true))
event_def = unquote(Macro.escape(event_def, unquote: true))
guard = unquote(Macro.escape(opts[:when]))
end
end

defp define_interface do
quote bind_quoted: [] do
unless event_name == :_ or MapSet.member?(@declared_events, {event_name, length(args)}) do
interface_args =
Enum.reduce(args, {0, []}, fn _, {index, args} ->
{
index + 1,
[{:"arg#{index}", [], nil} | args]
}
end)
|> elem(1)
|> Enum.reverse()

body =
quote do
transition(fsm, unquote(event_name), [unquote_splicing(interface_args)])
end

interface_args = [quote(do: fsm) | interface_args]

if private do
defp unquote(event_name)(unquote_splicing(interface_args)), do: unquote(body)
else
def unquote(event_name)(unquote_splicing(interface_args)), do: unquote(body)
end

@declared_events MapSet.put(@declared_events, {event_name, length(args)})
end
end
end

defp implement_transition do
quote bind_quoted: [] do
transition_args = [
if @declaring_state do
quote do
%__MODULE__{
state: unquote(@declaring_state) = unquote(state_arg),
data: unquote(data_arg)
} = fsm
end
else
quote do
%__MODULE__{state: unquote(state_arg), data: unquote(data_arg)} = fsm
end
end,
quote do
unquote(if event_name == :_, do: quote(do: _), else: event_name) = unquote(event_arg)
end,
quote do
unquote(if event_name == :_, do: quote(do: _), else: args) = unquote(args_arg)
end
]

body = quote(do: change_state(fsm, unquote(event_def)))

if guard do
def transition(unquote_splicing(transition_args)) when unquote(guard), do: unquote(body)
else
def transition(unquote_splicing(transition_args)), do: unquote(body)
end
end
end
end
30 changes: 15 additions & 15 deletions lib/event_store/sql/statements/insert_events.sql.eex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%
<%!--
# Elixir template variables:
# schema - string
# stream_id - integer
Expand All @@ -18,15 +18,15 @@
# 9 - created_at - timestamp
# 10 - index - integer
# 11 - stream_version - integer
%>
--%>

WITH
<%
<%!--
# create a table variable with:
# event_id - uuid - the id for the new event
# index - integer - the increase in the stream version for any stream it is linked to
# stream_version - integer - the final stream version after all of the events have been inserted
%>
--%>
new_events_indexes (event_id, index, stream_version) AS (
VALUES
<%= for i <- 0..(number_of_events - 1) do %>
Expand All @@ -35,11 +35,11 @@ WITH
<% end %>
),
events AS (
<%
<%!--
# insert the new events into the events table
# using the 7 bind variables from 3 to 9 inclusive
# n.b.: the bind for the event_id is re-generated here
%>
--%>
INSERT INTO "<%= schema %>".events
(
event_id,
Expand All @@ -57,18 +57,18 @@ WITH
<% end %>
),
stream AS (
<% # Increase the version to the stream version given %>
<%!-- Increase the version to the stream version given --%>
<%= cond do %>
<% stream_id -> %>
UPDATE "<%= schema %>".streams
SET stream_version = stream_version + $2::bigint
WHERE stream_id = $1::bigint
returning stream_id
<% created_at -> %>
<%
<%!--
# the event created_at date has been provided as the last bind variable
# use that instead of generating one
%>
--%>
INSERT INTO "<%= schema %>".streams (stream_uuid, stream_version, created_at)
VALUES ($1, $2::bigint, $<%= number_of_events*9 + 3 %>)
returning stream_id
Expand All @@ -79,14 +79,14 @@ WITH
<% end %>
),
source_stream_events AS (
<%
<%!--
# link the new events into it's source stream
# we're using the passed in event_ids rather than reading/joining from tables
# each insert uses the stream_version calculated for the corresponding event
# we're joining here, so we'll get the product of:
# the stream (1)
# the rows in the table variable (number_of_events)
%>
--%>
INSERT INTO "<%= schema %>".stream_events
(
event_id,
Expand All @@ -104,21 +104,21 @@ WITH
FROM new_events_indexes, stream
),
linked_stream AS (
<%
<%!--
# Update the all streams version by the number of events
# This is the value of the expected version at append time + the number of events
# Returns the version before the update
%>
--%>
UPDATE "<%= schema %>".streams
SET stream_version = stream_version + $2::bigint
WHERE stream_id = 0
RETURNING stream_version - $2::bigint as initial_stream_version
),
linked_stream_events AS (
<%
<%!--
# Link the new events into the $all stream
# 1 row for each event
%>
--%>
INSERT INTO "<%= schema %>".stream_events
(
event_id,
Expand Down
9 changes: 8 additions & 1 deletion lib/event_store/storage/snapshot.ex
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,14 @@ defmodule EventStore.Storage.Snapshot do
end
end

defp to_snapshot_from_row([source_uuid, source_version, source_type, data, metadata, created_at]) do
defp to_snapshot_from_row([
source_uuid,
source_version,
source_type,
data,
metadata,
created_at
]) do
%SnapshotData{
source_uuid: source_uuid,
source_version: source_version,
Expand Down
Loading
Loading