Packages

OT.Server provides a generic server for submitting operations in an operational transformation system.

Current section

Files

Jump to
ot_server lib ot server ets_adapter.ex
Raw

lib/ot/server/ets_adapter.ex

defmodule OT.Server.ETSAdapter do
@moduledoc """
This is an OT server
"""
@behaviour OT.Server.Adapter
use GenServer
@ops_table :ot_ops
@data_table :ot_data
defmodule RollbackError do
defexception [:message]
def exception(error) do
%__MODULE__{message: inspect(error)}
end
end
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@impl GenServer
def init(_) do
:ets.new(@data_table, [:named_table, :set, :public])
:ets.new(@ops_table, [:named_table, :ordered_set, :public])
{:ok, []}
end
@impl OT.Server.Adapter
def transact(func) do
GenServer.call(__MODULE__, {:transact, func})
end
@impl OT.Server.Adapter
def rollback(err) do
raise RollbackError, err
end
@impl OT.Server.Adapter
def get_conflicting_operations(%{id: id}, op_vsn) do
# Compiled from:
# :ets.fun2ms(fn {{^id, vsn}, op} when vsn >= op_vsn ->
# {op, vsn}
# end)
match_spec = [{
{{:"$1", :"$2"}, :"$3"},
[{:>=, :"$2", {:const, op_vsn}}, {:"=:=", {:const, id}, :"$1"}],
[{{:"$3", :"$2"}}]
}]
:ets.select(@ops_table, match_spec)
end
@impl OT.Server.Adapter
def get_datum(id) do
@data_table
|> :ets.lookup(id)
|> case do
[{^id, datum}] -> {:ok, datum}
_ -> {:error, :not_found}
end
end
@impl OT.Server.Adapter
def handle_submit_error({:error, :version_mismatch}, _, _) do
:retry
end
def handle_submit_error(err, _, _) do
err
end
@impl OT.Server.Adapter
def insert_operation(%{id: id}, {op, vsn}, _meta) do
if :ets.insert_new(@ops_table, {{id, vsn}, op}) do
{:ok, {op, vsn}}
else
{:error, :version_mismatch}
end
end
@impl OT.Server.Adapter
def update_datum(datum, content) do
datum = Map.put(datum, :content, content)
if :ets.insert(@data_table, {datum[:id], datum}) do
{:ok, datum}
else
{:error, :update_failed}
end
end
@impl GenServer
def handle_call({:transact, func}, _, state) do
{:reply, {:ok, func.()}, state}
end
end