diff --git a/src/khepri_machine.erl b/src/khepri_machine.erl
index 7b196535..116ae7dd 100644
--- a/src/khepri_machine.erl
+++ b/src/khepri_machine.erl
@@ -51,6 +51,9 @@
%%
%%
Moved the expiration of dedups to the `tick' aux effect (see {@link
%% handle_aux/5}). This also introduces a new command `#drop_dedups{}'.
+%% Added the `delete_reason' to the list of properties that can be
+%% retured. It is returned by default if the effective machine version is 2 or
+%% more.
%%
%%
%%
@@ -98,8 +101,8 @@
%% For internal use only.
-export([clear_cache/1,
ack_triggers_execution/2,
- split_query_options/1,
- split_command_options/1,
+ split_query_options/2,
+ split_command_options/2,
split_put_options/1,
insert_or_update_node/6,
delete_matching_nodes/4,
@@ -118,7 +121,8 @@
get_projections/1,
has_projection/2,
get_metrics/1,
- get_dedups/1]).
+ get_dedups/1,
+ get_store_id/1]).
-ifdef(TEST).
-export([do_process_sync_command/3,
@@ -283,7 +287,7 @@ fold(StoreId, PathPattern, Fun, Acc, Options)
is_function(Fun, 3) ->
PathPattern1 = khepri_path:from_string(PathPattern),
khepri_path:ensure_is_valid(PathPattern1),
- {QueryOptions, TreeOptions} = split_query_options(Options),
+ {QueryOptions, TreeOptions} = split_query_options(StoreId, Options),
Query = fun(State) ->
Tree = get_tree(State),
try
@@ -348,7 +352,8 @@ put(StoreId, PathPattern, Payload, Options)
PathPattern1 = khepri_path:from_string(PathPattern),
khepri_path:ensure_is_valid(PathPattern1),
Payload1 = khepri_payload:prepare(Payload),
- {CommandOptions, TreeAndPutOptions} = split_command_options(Options),
+ {CommandOptions, TreeAndPutOptions} = split_command_options(
+ StoreId, Options),
Command = #put{path = PathPattern1,
payload = Payload1,
options = TreeAndPutOptions},
@@ -376,7 +381,7 @@ put(_StoreId, PathPattern, Payload, _Options) ->
delete(StoreId, PathPattern, Options) when ?IS_KHEPRI_STORE_ID(StoreId) ->
PathPattern1 = khepri_path:from_string(PathPattern),
khepri_path:ensure_is_valid(PathPattern1),
- {CommandOptions, TreeOptions} = split_command_options(Options),
+ {CommandOptions, TreeOptions} = split_command_options(StoreId, Options),
%% TODO: Ensure `PutOptions' are not set this map.
Command = #delete{path = PathPattern1,
options = TreeOptions},
@@ -722,14 +727,16 @@ get_projections_state(StoreId, Options)
end,
process_query(StoreId, Query, Options).
--spec split_query_options(Options) -> {QueryOptions, TreeOptions} when
+-spec split_query_options(StoreId, Options) ->
+ {QueryOptions, TreeOptions} when
+ StoreId :: khepri:store_id(),
Options :: QueryOptions | TreeOptions,
QueryOptions :: khepri:query_options(),
TreeOptions :: khepri:tree_options().
%% @private
-split_query_options(Options) ->
- Options1 = set_default_options(Options),
+split_query_options(StoreId, Options) ->
+ Options1 = set_default_options(StoreId, Options),
maps:fold(
fun
(Option, Value, {Q, T}) when
@@ -748,15 +755,16 @@ split_query_options(Options) ->
{Q, T1}
end, {#{}, #{}}, Options1).
--spec split_command_options(Options) ->
+-spec split_command_options(StoreId, Options) ->
{CommandOptions, TreeAndPutOptions} when
+ StoreId :: khepri:store_id(),
Options :: CommandOptions | TreeAndPutOptions,
CommandOptions :: khepri:command_options(),
TreeAndPutOptions :: khepri:tree_options() | khepri:put_options().
%% @private
-split_command_options(Options) ->
- Options1 = set_default_options(Options),
+split_command_options(StoreId, Options) ->
+ Options1 = set_default_options(StoreId, Options),
maps:fold(
fun
(Option, Value, {C, TP}) when
@@ -799,7 +807,7 @@ split_put_options(TreeAndPutOptions) ->
{T1, P}
end, {#{}, #{}}, TreeAndPutOptions).
-set_default_options(Options) ->
+set_default_options(StoreId, Options) ->
%% By default, return payload-related properties. The caller can set
%% `props_to_return' to an empty map to get a minimal return value.
Options1 = case Options of
@@ -808,7 +816,21 @@ set_default_options(Options) ->
_ ->
Options#{props_to_return => ?DEFAULT_PROPS_TO_RETURN}
end,
- Options1.
+ %% We need to remove `delete_reason' from the list if the whole cluster is
+ %% still using a machine version that doesn't know about it. Otherwise old
+ %% versions of Khepri will crash when gathering the properties.
+ PropsToReturn0 = maps:get(props_to_return, Options1),
+ PropsToReturn1 = case effective_version(StoreId) of
+ {ok, EffectiveMacVer} when EffectiveMacVer >= 2 ->
+ PropsToReturn0;
+ _ ->
+ %% `delete_reason' was added in machine version
+ %% 2. Also, previous versions didn't ignore
+ %% unknown props_to_return and crashed.
+ PropsToReturn0 -- [delete_reason]
+ end,
+ Options2 = Options1#{props_to_return => PropsToReturn1},
+ Options2.
-spec process_command(StoreId, Command, Options) -> Ret when
StoreId :: khepri:store_id(),
@@ -1214,11 +1236,12 @@ can_skip_fence_preliminary_query(StoreId) ->
-spec init(Params) -> State when
Params :: machine_init_args(),
- State :: state().
+ State :: khepri_machine_v0:state().
%% @private
init(Params) ->
- %% Initialize the state.
+ %% Initialize the state. This function always return the oldest supported
+ %% state format.
State = khepri_machine_v0:init(Params),
%% Create initial "schema" if provided.
@@ -1742,7 +1765,7 @@ which_module(0) -> ?MODULE.
EffectiveMacVer :: ra_machine:version().
%% @doc Returns the effective state machine version of the local Ra server.
-effective_version(StoreId) ->
+effective_version(StoreId) when ?IS_KHEPRI_STORE_ID(StoreId) ->
ThisNode = node(),
RaServer = khepri_cluster:node_to_member(StoreId, ThisNode),
case ra_counters:counters(RaServer, [effective_machine_version]) of
@@ -2392,6 +2415,17 @@ set_dedups(#khepri_machine{} = State, Dedups) ->
set_dedups(State, _Dedups) ->
State.
+-spec get_store_id(State) -> StoreId when
+ State :: khepri_machine:state(),
+ StoreId :: khepri:store_id().
+%% @doc Returns the store ID from the given state.
+%%
+%% @private
+
+get_store_id(State) ->
+ #config{store_id = StoreId} = get_config(State),
+ StoreId.
+
-ifdef(TEST).
-spec make_virgin_state(Params) -> State when
Params :: khepri_machine:machine_init_args(),
diff --git a/src/khepri_tx.erl b/src/khepri_tx.erl
index a9a4d83c..78b5fa54 100644
--- a/src/khepri_tx.erl
+++ b/src/khepri_tx.erl
@@ -466,9 +466,11 @@ count(PathPattern) ->
count(PathPattern, Options) ->
PathPattern1 = khepri_tx_adv:path_from_string(PathPattern),
{State, _SideEffects} = khepri_tx_adv:get_tx_state(),
+ StoreId = khepri_machine:get_store_id(State),
Tree = khepri_machine:get_tree(State),
Fun = fun khepri_tree:count_node_cb/3,
- {_QueryOptions, TreeOptions} = khepri_machine:split_query_options(Options),
+ {_QueryOptions, TreeOptions} =
+ khepri_machine:split_query_options(StoreId, Options),
TreeOptions1 = TreeOptions#{expect_specific_node => false},
Ret = khepri_tree:fold(Tree, PathPattern1, Fun, 0, TreeOptions1),
case Ret of
@@ -517,8 +519,10 @@ fold(PathPattern, Fun, Acc) ->
fold(PathPattern, Fun, Acc, Options) ->
PathPattern1 = khepri_tx_adv:path_from_string(PathPattern),
{State, _SideEffects} = khepri_tx_adv:get_tx_state(),
+ StoreId = khepri_machine:get_store_id(State),
Tree = khepri_machine:get_tree(State),
- {_QueryOptions, TreeOptions} = khepri_machine:split_query_options(Options),
+ {_QueryOptions, TreeOptions} =
+ khepri_machine:split_query_options(StoreId, Options),
TreeOptions1 = TreeOptions#{expect_specific_node => false},
Ret = khepri_tree:fold(Tree, PathPattern1, Fun, Acc, TreeOptions1),
case Ret of
diff --git a/src/khepri_tx_adv.erl b/src/khepri_tx_adv.erl
index 5ab4efa0..9e193aab 100644
--- a/src/khepri_tx_adv.erl
+++ b/src/khepri_tx_adv.erl
@@ -129,9 +129,11 @@ get_many(PathPattern, Options) ->
do_get_many(PathPattern, Fun, Acc, Options) ->
PathPattern1 = path_from_string(PathPattern),
- {_QueryOptions, TreeOptions} = khepri_machine:split_query_options(Options),
{State, _SideEffects} = get_tx_state(),
+ StoreId = khepri_machine:get_store_id(State),
Tree = khepri_machine:get_tree(State),
+ {_QueryOptions, TreeOptions} =
+ khepri_machine:split_query_options(StoreId, Options),
Ret = khepri_tree:fold(Tree, PathPattern1, Fun, Acc, TreeOptions),
case Ret of
{error, ?khepri_exception(_, _) = Exception} ->
@@ -210,14 +212,16 @@ put_many(PathPattern, Data, Options) ->
ensure_updates_are_allowed(),
PathPattern1 = path_from_string(PathPattern),
Payload1 = khepri_payload:wrap(Data),
+ {State, _SideEffects} = get_tx_state(),
+ StoreId = khepri_machine:get_store_id(State),
{_CommandOptions, TreeAndPutOptions} =
- khepri_machine:split_command_options(Options),
+ khepri_machine:split_command_options(StoreId, Options),
{TreeOptions, PutOptions} =
khepri_machine:split_put_options(TreeAndPutOptions),
%% TODO: Ensure `CommandOptions' is unset.
- Fun = fun(State, SideEffects) ->
+ Fun = fun(State1, SideEffects) ->
khepri_machine:insert_or_update_node(
- State, PathPattern1, Payload1, PutOptions, TreeOptions,
+ State1, PathPattern1, Payload1, PutOptions, TreeOptions,
SideEffects)
end,
handle_state_for_call(Fun).
@@ -401,13 +405,15 @@ delete_many(PathPattern) ->
delete_many(PathPattern, Options) ->
ensure_updates_are_allowed(),
PathPattern1 = path_from_string(PathPattern),
+ {State, _SideEffects} = get_tx_state(),
+ StoreId = khepri_machine:get_store_id(State),
{_CommandOptions, TreeOptions} =
- khepri_machine:split_command_options(Options),
+ khepri_machine:split_command_options(StoreId, Options),
%% TODO: Ensure `CommandOptions' is empty and `TreeOptions' doesn't
%% contains put options.
- Fun = fun(State, SideEffects) ->
+ Fun = fun(State1, SideEffects) ->
khepri_machine:delete_matching_nodes(
- State, PathPattern1, TreeOptions, SideEffects)
+ State1, PathPattern1, TreeOptions, SideEffects)
end,
handle_state_for_call(Fun).