Current section

Files

Jump to
aarondb src aarondb@command.erl
Raw

src/aarondb@command.erl

-module(aarondb@command).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aarondb/command.gleam").
-export([new/0, apply/3, replay_hash/1, read/3, last_applied/1]).
-export_type([consistency/0, command/0, command_request/0, command_result/0, command_error/0, value/0, applied/0, state/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" # command — deterministic replicated command-state-machine reference\n"
"\n"
" This module deliberately has no transport, leader election, or local DB\n"
" integration. A future consensus runtime supplies the committed index; this\n"
" module deterministically applies the same command on every member.\n"
).
-type consistency() :: local | lease_read | linearizable.
-type command() :: {put, binary(), binary()} |
{compare_and_set, binary(), gleam@option:option(binary()), binary()} |
{issue_fence, binary()}.
-type command_request() :: {command_request, binary(), command()}.
-type command_result() :: {written, binary(), binary()} |
{cas_applied, binary(), gleam@option:option(binary()), binary()} |
{cas_rejected, binary(), gleam@option:option(binary())} |
{fence_issued, binary(), integer()}.
-type command_error() :: {idempotency_payload_mismatch, binary()} |
{invalid_committed_index, integer(), integer()}.
-type value() :: {value, binary(), binary()}.
-type applied() :: {applied, binary(), binary(), command_result()}.
-type state() :: {state,
list(value()),
list({binary(), integer()}),
list(applied()),
integer()}.
-file("src/aarondb/command.gleam", 56).
-spec new() -> state().
new() ->
{state, [], [], [], -1}.
-file("src/aarondb/command.gleam", 286).
-spec int_string(integer()) -> binary().
int_string(Value) ->
gleam@string:inspect(Value).
-file("src/aarondb/command.gleam", 167).
-spec frame(binary()) -> binary().
frame(Value) ->
<<<<(int_string(string:length(Value)))/binary, ":"/utf8>>/binary,
Value/binary>>.
-file("src/aarondb/command.gleam", 171).
-spec option_fingerprint(gleam@option:option(binary())) -> binary().
option_fingerprint(Value) ->
case Value of
none ->
<<"none"/utf8>>;
{some, Content} ->
<<"some:"/utf8, (frame(Content))/binary>>
end.
-file("src/aarondb/command.gleam", 158).
-spec fingerprint(command()) -> binary().
fingerprint(Command) ->
case Command of
{put, Key, Value} ->
<<<<"put:"/utf8, (frame(Key))/binary>>/binary,
(frame(Value))/binary>>;
{compare_and_set, Key@1, Expected, Replacement} ->
<<<<<<"cas:"/utf8, (frame(Key@1))/binary>>/binary,
(option_fingerprint(Expected))/binary>>/binary,
(frame(Replacement))/binary>>;
{issue_fence, Resource} ->
<<"fence:"/utf8, (frame(Resource))/binary>>
end.
-file("src/aarondb/command.gleam", 226).
-spec put_fence(list({binary(), integer()}), binary(), integer()) -> list({binary(),
integer()}).
put_fence(Fences, Resource, Token) ->
case Fences of
[] ->
[{Resource, Token}];
[{Saved_resource, Saved_token} | Rest] ->
case Saved_resource =:= Resource of
true ->
[{Resource, Token} | Rest];
false ->
[{Saved_resource, Saved_token} |
put_fence(Rest, Resource, Token)]
end
end.
-file("src/aarondb/command.gleam", 215).
-spec fence_for(list({binary(), integer()}), binary()) -> integer().
fence_for(Fences, Resource) ->
case Fences of
[] ->
0;
[{Saved_resource, Token} | Rest] ->
case Saved_resource =:= Resource of
true ->
Token;
false ->
fence_for(Rest, Resource)
end
end.
-file("src/aarondb/command.gleam", 189).
-spec put_value(list(value()), binary(), binary()) -> list(value()).
put_value(Values, Wanted, Replacement) ->
case Values of
[] ->
[{value, Wanted, Replacement}];
[{value, Key, Value} | Rest] ->
case Key =:= Wanted of
true ->
[{value, Wanted, Replacement} | Rest];
false ->
[{value, Key, Value} | put_value(Rest, Wanted, Replacement)]
end
end.
-file("src/aarondb/command.gleam", 178).
-spec get_value(list(value()), binary()) -> gleam@option:option(binary()).
get_value(Values, Wanted) ->
case Values of
[] ->
none;
[{value, Key, Value} | Rest] ->
case Key =:= Wanted of
true ->
{some, Value};
false ->
get_value(Rest, Wanted)
end
end.
-file("src/aarondb/command.gleam", 125).
-spec execute(command(), list(value()), list({binary(), integer()})) -> {list(value()),
list({binary(), integer()}),
command_result()}.
execute(Command, Values, Fences) ->
case Command of
{put, Key, Value} ->
{put_value(Values, Key, Value), Fences, {written, Key, Value}};
{compare_and_set, Key@1, Expected, Replacement} ->
Actual = get_value(Values, Key@1),
case Actual =:= Expected of
true ->
{put_value(Values, Key@1, Replacement),
Fences,
{cas_applied, Key@1, Actual, Replacement}};
false ->
{Values, Fences, {cas_rejected, Key@1, Actual}}
end;
{issue_fence, Resource} ->
Token = fence_for(Fences, Resource) + 1,
{Values,
put_fence(Fences, Resource, Token),
{fence_issued, Resource, Token}}
end.
-file("src/aarondb/command.gleam", 106).
-spec apply_new(integer(), command_request(), state()) -> {ok,
{state(), command_result()}} |
{error, command_error()}.
apply_new(Index, Request, State) ->
{Values, Fences, Result} = execute(
erlang:element(3, Request),
erlang:element(2, State),
erlang:element(3, State)
),
Updated = {state,
Values,
Fences,
lists:append(
erlang:element(4, State),
[{applied,
erlang:element(2, Request),
fingerprint(erlang:element(3, Request)),
Result}]
),
Index},
{ok, {Updated, Result}}.
-file("src/aarondb/command.gleam", 204).
-spec find_applied(list(applied()), binary()) -> gleam@option:option(applied()).
find_applied(Applied, Key) ->
case Applied of
[] ->
none;
[{applied, Saved_key, Saved_fingerprint, Saved_result} | Rest] ->
case Saved_key =:= Key of
true ->
{some,
{applied, Saved_key, Saved_fingerprint, Saved_result}};
false ->
find_applied(Rest, Key)
end
end.
-file("src/aarondb/command.gleam", 62).
?DOC(
" The committed index must advance exactly once. Retried idempotency requests\n"
" return their original result without applying another mutation.\n"
).
-spec apply(integer(), command_request(), state()) -> {ok,
{state(), command_result()}} |
{error, command_error()}.
apply(Index, Request, State) ->
case find_applied(erlang:element(4, State), erlang:element(2, Request)) of
{some, {applied, _, Saved_fingerprint, Result}} ->
case Saved_fingerprint =:= fingerprint(erlang:element(3, Request)) of
true ->
{ok, {State, Result}};
false ->
{error,
{idempotency_payload_mismatch,
erlang:element(2, Request)}}
end;
none ->
case Index =:= (erlang:element(5, State) + 1) of
false ->
{error,
{invalid_committed_index,
Index,
erlang:element(5, State)}};
true ->
apply_new(Index, Request, State)
end
end.
-file("src/aarondb/command.gleam", 271).
-spec result_fingerprint(command_result()) -> binary().
result_fingerprint(Result) ->
case Result of
{written, Key, Value} ->
<<<<"written"/utf8, (frame(Key))/binary>>/binary,
(frame(Value))/binary>>;
{cas_applied, Key@1, Previous, Value@1} ->
<<<<<<"cas-applied"/utf8, (frame(Key@1))/binary>>/binary,
(option_fingerprint(Previous))/binary>>/binary,
(frame(Value@1))/binary>>;
{cas_rejected, Key@2, Actual} ->
<<<<"cas-rejected"/utf8, (frame(Key@2))/binary>>/binary,
(option_fingerprint(Actual))/binary>>;
{fence_issued, Resource, Token} ->
<<<<"fence"/utf8, (frame(Resource))/binary>>/binary,
(int_string(Token))/binary>>
end.
-file("src/aarondb/command.gleam", 260).
-spec applied_fingerprint(list(applied())) -> binary().
applied_fingerprint(Applied) ->
case Applied of
[] ->
<<""/utf8>>;
[{applied, Key, Hash, Result} | Rest] ->
<<<<<<(frame(Key))/binary, (frame(Hash))/binary>>/binary,
(result_fingerprint(Result))/binary>>/binary,
(applied_fingerprint(Rest))/binary>>
end.
-file("src/aarondb/command.gleam", 252).
-spec fences_fingerprint(list({binary(), integer()})) -> binary().
fences_fingerprint(Fences) ->
case Fences of
[] ->
<<""/utf8>>;
[{Resource, Token} | Rest] ->
<<<<(frame(Resource))/binary, (int_string(Token))/binary>>/binary,
(fences_fingerprint(Rest))/binary>>
end.
-file("src/aarondb/command.gleam", 244).
-spec values_fingerprint(list(value())) -> binary().
values_fingerprint(Values) ->
case Values of
[] ->
<<""/utf8>>;
[{value, Key, Value} | Rest] ->
<<<<(frame(Key))/binary, (frame(Value))/binary>>/binary,
(values_fingerprint(Rest))/binary>>
end.
-file("src/aarondb/command.gleam", 83).
?DOC(
" The hash is intentionally constructed from closed, ordered data. It is an\n"
" audit/replay fingerprint, not a cryptographic signature.\n"
).
-spec replay_hash(state()) -> binary().
replay_hash(State) ->
<<<<<<<<<<<<<<"state|"/utf8, (int_string(erlang:element(5, State)))/binary>>/binary,
"|values|"/utf8>>/binary,
(values_fingerprint(erlang:element(2, State)))/binary>>/binary,
"|fences|"/utf8>>/binary,
(fences_fingerprint(erlang:element(3, State)))/binary>>/binary,
"|applied|"/utf8>>/binary,
(applied_fingerprint(erlang:element(4, State)))/binary>>.
-file("src/aarondb/command.gleam", 94).
-spec read(state(), binary(), consistency()) -> {consistency(),
gleam@option:option(binary())}.
read(State, Key, Mode) ->
{Mode, get_value(erlang:element(2, State), Key)}.
-file("src/aarondb/command.gleam", 102).
-spec last_applied(state()) -> integer().
last_applied(State) ->
erlang:element(5, State).