Current section
Files
Jump to
Current section
Files
src/aarondb@transactor.erl
-module(aarondb@transactor).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aarondb/transactor.gleam").
-export([compute_next_state/4, start_with_timeout/2, start/1, start_named/2, start_distributed/2, retract_entity/3, log_query/2, get_state/1, set_schema_with_timeout/4, set_schema/3, register_function/3, register_composite/2, register_predicate/3, store_rule/2, set_config/2, transact_with_timeout/3, transact/2, retract/2, subscribe/1]).
-export_type([message/0]).
-type message() :: {transact,
list({aarondb@fact:eid(), binary(), aarondb@fact:value()}),
gleam@option:option(integer()),
gleam@erlang@process:subject({ok, aarondb@shared@state:db_state()} |
{error, binary()})} |
{retract,
list({aarondb@fact:eid(), binary(), aarondb@fact:value()}),
gleam@option:option(integer()),
gleam@erlang@process:subject({ok, aarondb@shared@state:db_state()} |
{error, binary()})} |
{get_state, gleam@erlang@process:subject(aarondb@shared@state:db_state())} |
{set_schema,
binary(),
aarondb@fact:attribute_config(),
gleam@erlang@process:subject({ok, nil} | {error, binary()})} |
{register_function,
binary(),
fun((aarondb@shared@state:db_state(), integer(), integer(), list(aarondb@fact:value())) -> list({aarondb@fact:eid(),
binary(),
aarondb@fact:value()})),
gleam@erlang@process:subject(nil)} |
{register_predicate,
binary(),
fun((aarondb@fact:value()) -> boolean()),
gleam@erlang@process:subject(nil)} |
{register_composite,
list(binary()),
gleam@erlang@process:subject({ok, nil} | {error, binary()})} |
{store_rule,
aarondb@shared@ast:rule(),
gleam@erlang@process:subject({ok, nil} | {error, binary()})} |
{set_reactive,
gleam@erlang@process:subject(aarondb@shared@state:reactive_message())} |
{join, gleam@erlang@process:pid_()} |
{sync_datoms, list(aarondb@fact:datom())} |
{compact, gleam@erlang@process:subject(nil)} |
{set_config,
aarondb@shared@state:config(),
gleam@erlang@process:subject(nil)} |
{sync, gleam@erlang@process:subject(nil)} |
{boot,
gleam@option:option(binary()),
aarondb@storage:storage_adapter(),
gleam@erlang@process:subject({ok, nil} | {error, binary()})} |
{register_index_adapter,
aarondb@shared@state:index_adapter(),
gleam@erlang@process:subject(nil)} |
{create_index,
binary(),
binary(),
binary(),
gleam@erlang@process:subject({ok, nil} | {error, binary()})} |
{create_b_m25_index,
binary(),
gleam@erlang@process:subject({ok, nil} | {error, binary()})} |
{subscribe, gleam@erlang@process:subject(list(aarondb@fact:datom()))} |
{prune, integer(), list(binary()), gleam@erlang@process:subject(integer())} |
{retract_entity,
aarondb@fact:entity_id(),
gleam@erlang@process:subject({ok, aarondb@shared@state:db_state()} |
{error, binary()})} |
tick |
{log_query,
aarondb@shared@state:query_context(),
gleam@erlang@process:subject(nil)}.
-file("src/aarondb/transactor.gleam", 169).
-spec lifecycle_loop(gleam@erlang@process:subject(message())) -> any().
lifecycle_loop(Parent) ->
gleam_erlang_ffi:sleep(5000),
gleam@erlang@process:send(Parent, tick),
lifecycle_loop(Parent).
-file("src/aarondb/transactor.gleam", 308).
-spec compute_next_state(
aarondb@shared@state:db_state(),
list({aarondb@fact:eid(), binary(), aarondb@fact:value()}),
gleam@option:option(integer()),
aarondb@fact:operation()
) -> {ok, {aarondb@shared@state:db_state(), list(aarondb@fact:datom())}} |
{error, binary()}.
compute_next_state(State, Facts, Valid_time, Op) ->
Tx_id = erlang:element(6, State) + 1,
Vt = gleam@option:unwrap(Valid_time, Tx_id),
Resolved_facts = aarondb@transactor@apply:resolve_transaction_functions(
State,
Tx_id,
Vt,
Facts
),
Datoms_res = gleam@list:fold_until(
Resolved_facts,
{ok, []},
fun(Acc_res, F) ->
Acc@1 = case Acc_res of
{ok, Acc} -> Acc;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"aarondb/transactor"/utf8>>,
function => <<"compute_next_state"/utf8>>,
line => 324,
value => _assert_fail,
start => 8898,
'end' => 8926,
pattern_start => 8909,
pattern_end => 8916})
end,
Eid_res = case erlang:element(1, F) of
{uid, Id} ->
{ok, Id};
{lookup, Lu} ->
{A, V} = Lu,
case A =:= <<"db/fn"/utf8>> of
true ->
{error,
<<"Unresolved transaction function: "/utf8,
(gleam@string:inspect(V))/binary>>};
false ->
_pipe = aarondb@index:get_entity_by_av(
erlang:element(5, State),
A,
V
),
gleam@result:replace_error(
_pipe,
<<"Lookup failed for "/utf8, A/binary>>
)
end
end,
case Eid_res of
{ok, Eid} ->
D = {datom,
Eid,
erlang:element(2, F),
erlang:element(3, F),
Tx_id,
erlang:length(Acc@1),
Vt,
Op},
{continue, {ok, [D | Acc@1]}};
{error, E} ->
{stop, {error, E}}
end
end
),
case Datoms_res of
{ok, Datoms} ->
Datoms@1 = lists:reverse(Datoms),
{Final_state, All_datoms, _} = gleam@list:fold(
Datoms@1,
{State, [], 0},
fun(Acc@2, D@1) ->
{Curr_state, Collected, Next_idx} = Acc@2,
{New_state, Side_effects, Updated_idx} = aarondb@transactor@apply:apply_datom(
Curr_state,
{datom,
erlang:element(2, D@1),
erlang:element(3, D@1),
erlang:element(4, D@1),
erlang:element(5, D@1),
Next_idx,
erlang:element(7, D@1),
erlang:element(8, D@1)},
Next_idx
),
{New_state,
lists:append(Side_effects, Collected),
Updated_idx}
end
),
All_datoms@1 = lists:reverse(All_datoms),
Validate_res = gleam@list:fold_until(
All_datoms@1,
{ok, nil},
fun(_, D@2) ->
case aarondb@transactor@validation:validate_datom(
State,
All_datoms@1,
D@2
) of
{ok, _} ->
{continue, {ok, nil}};
{error, E@1} ->
{stop, {error, E@1}}
end
end
),
case Validate_res of
{ok, _} ->
{ok,
{{db_state,
erlang:element(2, Final_state),
erlang:element(3, Final_state),
erlang:element(4, Final_state),
erlang:element(5, Final_state),
Tx_id,
erlang:element(7, Final_state),
erlang:element(8, Final_state),
erlang:element(9, Final_state),
erlang:element(10, Final_state),
erlang:element(11, Final_state),
erlang:element(12, Final_state),
erlang:element(13, Final_state),
erlang:element(14, Final_state),
erlang:element(15, Final_state),
erlang:element(16, Final_state),
erlang:element(17, Final_state),
erlang:element(18, Final_state),
erlang:element(19, Final_state),
erlang:element(20, Final_state),
erlang:element(21, Final_state),
erlang:element(22, Final_state),
erlang:element(23, Final_state),
erlang:element(24, Final_state),
erlang:element(25, Final_state)},
All_datoms@1}};
{error, E@2} ->
{error, E@2}
end;
{error, E@3} ->
{error, E@3}
end.
-file("src/aarondb/transactor.gleam", 399).
-spec handle_message(aarondb@shared@state:db_state(), message()) -> gleam@otp@actor:next(aarondb@shared@state:db_state(), message()).
handle_message(State, Msg) ->
case Msg of
{log_query, Ctx, Reply} ->
aarondb@transactor@messages:log_query(State, Ctx, Reply);
tick ->
gleam@otp@actor:continue(
aarondb@transactor@lifecycle:handle_tick(State)
);
{boot, Ets_name, _, Reply@1} ->
case Ets_name of
{some, Name} ->
aarondb@index@ets:init_tables(Name);
none ->
nil
end,
Initialized = aarondb_mnesia_ffi:init(),
case Initialized of
{ok, nil} ->
New_state = aarondb@transactor@runtime:recover_state(State),
gleam@erlang@process:send(Reply@1, {ok, nil}),
gleam@otp@actor:continue(New_state);
{error, Error} ->
gleam@erlang@process:send(Reply@1, {error, Error}),
gleam@otp@actor:continue(State)
end;
{transact, Facts, Vt, Reply_to} ->
aarondb@transactor@runtime:do_handle_transact(
State,
Facts,
Vt,
assert,
Reply_to,
fun compute_next_state/4
);
{retract, Facts@1, Vt@1, Reply_to@1} ->
aarondb@transactor@runtime:do_handle_transact(
State,
Facts@1,
Vt@1,
retract,
Reply_to@1,
fun compute_next_state/4
);
{retract_entity, Eid, Reply_to@2} ->
Datoms = case erlang:element(14, State) of
{some, Name@1} ->
aarondb@index@ets:lookup_datoms(
<<Name@1/binary, "_eavt"/utf8>>,
Eid
);
none ->
aarondb@index:filter_by_entity(
erlang:element(3, State),
Eid
)
end,
Facts@2 = gleam@list:map(
Datoms,
fun(D) ->
{{uid, erlang:element(2, D)},
erlang:element(3, D),
erlang:element(4, D)}
end
),
aarondb@transactor@runtime:do_handle_transact(
State,
Facts@2,
none,
retract,
Reply_to@2,
fun compute_next_state/4
);
{get_state, Reply_to@3} ->
gleam@erlang@process:send(Reply_to@3, State),
gleam@otp@actor:continue(State);
{set_schema, Attr, Config, Reply_to@4} ->
Error@1 = case erlang:element(2, Config) of
true ->
aarondb@transactor@schema:validate_unique(State, Attr);
false ->
none
end,
Error@2 = case Error@1 of
none ->
case erlang:element(5, Config) =:= one of
true ->
aarondb@transactor@schema:validate_cardinality_one(
State,
Attr
);
false ->
none
end;
{some, E} ->
{some, E}
end,
aarondb@transactor@messages:set_schema(
State,
Attr,
Config,
Error@2,
Reply_to@4
);
{register_function, Name@2, Func, Reply_to@5} ->
aarondb@transactor@messages:register_function(
State,
Name@2,
Func,
Reply_to@5
);
{register_predicate, Name@3, Pred, Reply_to@6} ->
aarondb@transactor@messages:register_predicate(
State,
Name@3,
Pred,
Reply_to@6
);
{register_composite, Attrs, Reply_to@7} ->
aarondb@transactor@messages:register_composite(
State,
Attrs,
aarondb@transactor@schema:validate_composite(State, Attrs),
Reply_to@7
);
{store_rule, Rule, Reply_to@8} ->
aarondb@transactor@messages:store_rule(
State,
Rule,
Reply_to@8,
fun compute_next_state/4
);
{subscribe, Reply_to@9} ->
aarondb@transactor@messages:subscribe(State, Reply_to@9);
{set_config, Config@1, Reply_to@10} ->
aarondb@transactor@messages:set_config(State, Config@1, Reply_to@10);
_ ->
gleam@otp@actor:continue(State)
end.
-file("src/aarondb/transactor.gleam", 95).
-spec do_start_named(
aarondb@storage:storage_adapter(),
boolean(),
gleam@option:option(binary()),
integer()
) -> {ok, gleam@erlang@process:subject(message())} |
{error, gleam@otp@actor:start_error()}.
do_start_named(Store, Is_distributed, Ets_name, Timeout_ms) ->
Reactive_subject@1 = case aarondb@reactive:start_link() of
{ok, Reactive_subject} -> Reactive_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"aarondb/transactor"/utf8>>,
function => <<"do_start_named"/utf8>>,
line => 101,
value => _assert_fail,
start => 3010,
'end' => 3065,
pattern_start => 3021,
pattern_end => 3041})
end,
Base_state = {db_state,
Store,
aarondb@index:new_index(),
aarondb@index:new_aindex(),
aarondb@index:new_avindex(),
0,
[],
maps:new(),
maps:new(),
[],
Reactive_subject@1,
[],
Is_distributed,
Ets_name,
aarondb@vec_index:new(),
maps:new(),
aarondb@index@art:new(),
maps:new(),
maps:new(),
maps:new(),
[],
maps:new(),
maps:new(),
{config, 1000, 1000, false, 10000},
[]},
Res = begin
_pipe = gleam@otp@actor:new(Base_state),
_pipe@1 = gleam@otp@actor:on_message(_pipe, fun handle_message/2),
gleam@otp@actor:start(_pipe@1)
end,
case Res of
{ok, Started} ->
Subj = erlang:element(3, Started),
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {boot, Ets_name, Store, Reply}),
case gleam@erlang@process:'receive'(Reply, Timeout_ms) of
{error, _} ->
{error, init_timeout};
{ok, {error, _}} ->
{error, init_timeout};
{ok, {ok, nil}} ->
Pid = aarondb_process_ffi:subject_to_pid(Subj),
_ = case Is_distributed of
true ->
nil;
false ->
_ = aarondb_global_ffi:register(
<<"aarondb_leader"/utf8>>,
Pid
),
nil
end,
_ = proc_lib:spawn_link(fun() -> lifecycle_loop(Subj) end),
{ok, Subj}
end;
{error, E} ->
{error, E}
end.
-file("src/aarondb/transactor.gleam", 88).
-spec start_with_timeout(aarondb@storage:storage_adapter(), integer()) -> {ok,
gleam@erlang@process:subject(message())} |
{error, gleam@otp@actor:start_error()}.
start_with_timeout(Store, Timeout_ms) ->
do_start_named(Store, false, none, Timeout_ms).
-file("src/aarondb/transactor.gleam", 68).
-spec start(aarondb@storage:storage_adapter()) -> {ok,
gleam@erlang@process:subject(message())} |
{error, gleam@otp@actor:start_error()}.
start(Store) ->
start_with_timeout(Store, 1000).
-file("src/aarondb/transactor.gleam", 74).
-spec start_named(binary(), aarondb@storage:storage_adapter()) -> {ok,
gleam@erlang@process:subject(message())} |
{error, gleam@otp@actor:start_error()}.
start_named(Name, Store) ->
do_start_named(Store, false, {some, Name}, 1000).
-file("src/aarondb/transactor.gleam", 81).
-spec start_distributed(binary(), aarondb@storage:storage_adapter()) -> {ok,
gleam@erlang@process:subject(message())} |
{error, gleam@otp@actor:start_error()}.
start_distributed(Name, Store) ->
do_start_named(Store, true, {some, Name}, 1000).
-file("src/aarondb/transactor.gleam", 175).
-spec retract_entity(
gleam@erlang@process:subject(message()),
aarondb@fact:entity_id(),
gleam@erlang@process:subject({ok, aarondb@shared@state:db_state()} |
{error, binary()})
) -> nil.
retract_entity(Subj, Eid, Reply) ->
gleam@erlang@process:send(Subj, {retract_entity, Eid, Reply}).
-file("src/aarondb/transactor.gleam", 183).
-spec log_query(
gleam@erlang@process:subject(message()),
aarondb@shared@state:query_context()
) -> nil.
log_query(Subj, Ctx) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {log_query, Ctx, Reply}),
_ = gleam@erlang@process:'receive'(Reply, 100),
nil.
-file("src/aarondb/transactor.gleam", 194).
-spec get_state(gleam@erlang@process:subject(message())) -> aarondb@shared@state:db_state().
get_state(Subj) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {get_state, Reply}),
State@1 = case gleam@erlang@process:'receive'(Reply, 5000) of
{ok, State} -> State;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"aarondb/transactor"/utf8>>,
function => <<"get_state"/utf8>>,
line => 197,
value => _assert_fail,
start => 5578,
'end' => 5629,
pattern_start => 5589,
pattern_end => 5598})
end,
State@1.
-file("src/aarondb/transactor.gleam", 209).
-spec set_schema_with_timeout(
gleam@erlang@process:subject(message()),
binary(),
aarondb@fact:attribute_config(),
integer()
) -> {ok, nil} | {error, binary()}.
set_schema_with_timeout(Subj, Attr, Config, Timeout_ms) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {set_schema, Attr, Config, Reply}),
case gleam@erlang@process:'receive'(Reply, Timeout_ms) of
{ok, Res} ->
Res;
{error, _} ->
{error, <<"Timeout setting schema"/utf8>>}
end.
-file("src/aarondb/transactor.gleam", 201).
-spec set_schema(
gleam@erlang@process:subject(message()),
binary(),
aarondb@fact:attribute_config()
) -> {ok, nil} | {error, binary()}.
set_schema(Subj, Attr, Config) ->
set_schema_with_timeout(Subj, Attr, Config, 5000).
-file("src/aarondb/transactor.gleam", 223).
-spec register_function(
gleam@erlang@process:subject(message()),
binary(),
fun((aarondb@shared@state:db_state(), integer(), integer(), list(aarondb@fact:value())) -> list({aarondb@fact:eid(),
binary(),
aarondb@fact:value()}))
) -> nil.
register_function(Subj, Name, Func) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {register_function, Name, Func, Reply}),
case gleam@erlang@process:'receive'(Reply, 5000) of
{ok, nil} -> nil;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"aarondb/transactor"/utf8>>,
function => <<"register_function"/utf8>>,
line => 230,
value => _assert_fail,
start => 6414,
'end' => 6463,
pattern_start => 6425,
pattern_end => 6432})
end,
nil.
-file("src/aarondb/transactor.gleam", 234).
-spec register_composite(
gleam@erlang@process:subject(message()),
list(binary())
) -> {ok, nil} | {error, binary()}.
register_composite(Subj, Attrs) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {register_composite, Attrs, Reply}),
case gleam@erlang@process:'receive'(Reply, 5000) of
{ok, Res} ->
Res;
{error, _} ->
{error, <<"Timeout registering composite"/utf8>>}
end.
-file("src/aarondb/transactor.gleam", 246).
-spec register_predicate(
gleam@erlang@process:subject(message()),
binary(),
fun((aarondb@fact:value()) -> boolean())
) -> nil.
register_predicate(Subj, Name, Pred) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {register_predicate, Name, Pred, Reply}),
case gleam@erlang@process:'receive'(Reply, 5000) of
{ok, nil} -> nil;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"aarondb/transactor"/utf8>>,
function => <<"register_predicate"/utf8>>,
line => 253,
value => _assert_fail,
start => 7010,
'end' => 7059,
pattern_start => 7021,
pattern_end => 7028})
end,
nil.
-file("src/aarondb/transactor.gleam", 257).
-spec store_rule(
gleam@erlang@process:subject(message()),
aarondb@shared@ast:rule()
) -> {ok, nil} | {error, binary()}.
store_rule(Subj, Rule) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {store_rule, Rule, Reply}),
case gleam@erlang@process:'receive'(Reply, 5000) of
{ok, Res} ->
Res;
{error, _} ->
{error, <<"Timeout storing rule"/utf8>>}
end.
-file("src/aarondb/transactor.gleam", 269).
-spec set_config(
gleam@erlang@process:subject(message()),
aarondb@shared@state:config()
) -> nil.
set_config(Subj, Config) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {set_config, Config, Reply}),
case gleam@erlang@process:'receive'(Reply, 5000) of
{ok, nil} -> nil;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"aarondb/transactor"/utf8>>,
function => <<"set_config"/utf8>>,
line => 272,
value => _assert_fail,
start => 7524,
'end' => 7573,
pattern_start => 7535,
pattern_end => 7542})
end,
nil.
-file("src/aarondb/transactor.gleam", 283).
-spec transact_with_timeout(
gleam@erlang@process:subject(message()),
list({aarondb@fact:eid(), binary(), aarondb@fact:value()}),
integer()
) -> {ok, aarondb@shared@state:db_state()} | {error, binary()}.
transact_with_timeout(Subj, Facts, Timeout_ms) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {transact, Facts, none, Reply}),
case gleam@erlang@process:'receive'(Reply, Timeout_ms) of
{ok, Res} ->
Res;
{error, _} ->
{error, <<"Transaction timeout"/utf8>>}
end.
-file("src/aarondb/transactor.gleam", 276).
-spec transact(
gleam@erlang@process:subject(message()),
list({aarondb@fact:eid(), binary(), aarondb@fact:value()})
) -> {ok, aarondb@shared@state:db_state()} | {error, binary()}.
transact(Subj, Facts) ->
transact_with_timeout(Subj, Facts, 5000).
-file("src/aarondb/transactor.gleam", 296).
-spec retract(
gleam@erlang@process:subject(message()),
list({aarondb@fact:eid(), binary(), aarondb@fact:value()})
) -> {ok, aarondb@shared@state:db_state()} | {error, binary()}.
retract(Subj, Facts) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {retract, Facts, none, Reply}),
case gleam@erlang@process:'receive'(Reply, 5000) of
{ok, Res} ->
Res;
{error, _} ->
{error, <<"Retraction timeout"/utf8>>}
end.
-file("src/aarondb/transactor.gleam", 511).
-spec subscribe(gleam@erlang@process:subject(message())) -> gleam@erlang@process:subject(list(aarondb@fact:datom())).
subscribe(Subj) ->
Reply = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Subj, {subscribe, Reply}),
Reply.