From 257cf328110a69618ad0cf35e3ea1c5bb2a40e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-S=C3=A9bastien=20P=C3=A9dron?= Date: Wed, 11 Dec 2024 11:16:15 +0100 Subject: [PATCH] khepri_tree: Ignore unknown props to return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Why] A future version of Khepri might introduce new props to return. If a Khepri cluster is being upgraded and several versions of the Khepri machine are running at the same time, an old node could be called with props to return it doesn’t know. [How] We simply ignore unknown props. In the process, we ignore `delete_reason` because it is set by another part of the code anyway. --- src/khepri.erl | 30 ++++++++++++++++++++++-------- src/khepri_tree.erl | 4 +++- test/queries.erl | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/khepri.erl b/src/khepri.erl index 07892a07..1e76b9e5 100644 --- a/src/khepri.erl +++ b/src/khepri.erl @@ -348,14 +348,8 @@ %% set, `condition' takes precedence and `favor' is ignored. -type tree_options() :: #{expect_specific_node => boolean(), - props_to_return => [payload_version | - child_list_version | - child_list_length | - child_names | - payload | - has_payload | - raw_payload | - delete_reason], + props_to_return => [known_prop_to_return() | + unknown_prop_to_return()], include_root_props => boolean()}. %% Options used during tree traversal. %% @@ -372,6 +366,26 @@ %% returned as well. %% +-type known_prop_to_return() :: payload_version | + child_list_version | + child_list_length | + child_names | + payload | + has_payload | + raw_payload | + delete_reason. +%% Name of a known property to return. +%% +%% To be used in {@link khepri:tree_options()} `props_to_return' list. + +-type unknown_prop_to_return() :: atom(). +%% Name of a property unknown to this version of Khepri. +%% +%% This can happen when a query is emitted by a newer version of Khepri that +%% added new properties to return. +%% +%% Can be seen in {@link khepri:tree_options()} `props_to_return' list. + -type put_options() :: #{keep_while => khepri_condition:keep_while()}. %% Options specific to updates. %% diff --git a/src/khepri_tree.erl b/src/khepri_tree.erl index f97cef57..3371a542 100644 --- a/src/khepri_tree.erl +++ b/src/khepri_tree.erl @@ -234,7 +234,9 @@ gather_node_props(#node{props = #{payload_version := PVersion, end; (raw_payload, Acc) -> Acc#{raw_payload => Payload}; - (delete_reason, Acc) -> + (_Unknown, Acc) -> + %% We ignore props we don't know about. It might be a new one in + %% a future version of the machine. Acc end, #{}, WantedProps); gather_node_props(#node{}, _Options) -> diff --git a/test/queries.erl b/test/queries.erl index ed84b82b..ab878799 100644 --- a/test/queries.erl +++ b/test/queries.erl @@ -399,3 +399,17 @@ include_child_names_in_query_response_test() -> child_list_version => 2, child_names => [bar, quux]}}}, Ret). + +use_an_unknown_prop_to_return_test() -> + Commands = [#put{path = [foo], + payload = khepri_payload:data(value)}], + S0 = khepri_machine:init(?MACH_PARAMS(Commands)), + Tree = khepri_machine:get_tree(S0), + Ret = khepri_tree:find_matching_nodes( + Tree, [foo], + #{props_to_return => [payload, + future_prop_name]}), + + ?assertEqual( + {ok, #{[foo] => #{data => value}}}, + Ret).