Skip to content

Commit 564bca6

Browse files
committed
Feature flags: Handle plugins' feature flags only present on some nodes
Before this patch, when a node had a plugin which provided a feature flag, that node could not join a cluster because the other nodes didn't know about that plugin's feature flag. This was the same problem when an operator enabled a plugin present on some nodes (but not all) in a cluster and then wanted to enable the plugin's feature flags. This situation is fixed by paying attention to the Erlang applications (plugins) providing each feature flag, when we want to determine if two nodes are compatible. To achieve this and still maintain the same view & state on all nodes, when a node (re)joins a cluster, all feature flags from both sides are exchanged: if a feature flag on one side is provided by an application which is missing on the other side, that feature flag is added to the latter's registry. After this exchange, we proceed with the regular compatibility check. Therefore, feature flags provided by unknown applications are supported everywhere and thus won't interfere. Also in this patch is a fix of the registry generation: the way feature flag states were handled was incorrect: reinitializing the registry could loose states because `initialize_registry/3` would take the complete list of enabled feature flags. Now it is transformed to take a "diff": a map indicating which feature flags are enabled/disabled or marked as `state_changing`. We now store a map of those states inside the registry. One change of behavior with this patch is: feature flags are enabled by default only if it is a virgin node (it is the first time it starts or it was reset), even if it is a single non-clustered node. Finally, the testsuite for feature flags was expanded to cover various clustering and plugin activation situations. [#163796129]
1 parent 8fa76df commit 564bca6

File tree

11 files changed

+1276
-185
lines changed

11 files changed

+1276
-185
lines changed

src/rabbit.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ start_apps(Apps) ->
520520

521521
start_apps(Apps, RestartTypes) ->
522522
app_utils:load_applications(Apps),
523-
rabbit_feature_flags:initialize_registry(),
523+
ok = rabbit_feature_flags:refresh_feature_flags_after_app_load(Apps),
524524
start_loaded_apps(Apps, RestartTypes).
525525

526526
start_loaded_apps(Apps) ->

src/rabbit_feature_flags.erl

Lines changed: 604 additions & 137 deletions
Large diffs are not rendered by default.

src/rabbit_ff_registry.erl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
-export([get/1,
3333
list/1,
34+
states/0,
3435
is_supported/1,
3536
is_enabled/1,
3637
is_registry_initialized/0,
@@ -76,6 +77,23 @@ list(Which) ->
7677
true -> #{}
7778
end.
7879

80+
-spec states() -> rabbit_feature_flags:feature_states().
81+
%% @doc
82+
%% Returns the states of supported feature flags.
83+
%%
84+
%% Only the informations stored in the local registry is used to answer
85+
%% this call.
86+
%%
87+
%% @returns A map of feature flag states.
88+
89+
states() ->
90+
rabbit_feature_flags:initialize_registry(),
91+
%% See get/1 for an explanation of the case statement below.
92+
case is_registry_initialized() of
93+
false -> ?MODULE:states();
94+
true -> #{}
95+
end.
96+
7997
-spec is_supported(rabbit_feature_flags:feature_name()) -> boolean().
8098
%% @doc
8199
%% Returns if a feature flag is supported.

src/rabbit_mnesia.erl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ dir() -> mnesia:system_info(directory).
528528
%% nodes in the cluster already. It also updates the cluster status
529529
%% file.
530530
init_db(ClusterNodes, NodeType, CheckOtherNodes) ->
531+
NodeIsVirgin = is_virgin_node(),
531532
Nodes = change_extra_db_nodes(ClusterNodes, CheckOtherNodes),
532533
%% Note that we use `system_info' here and not the cluster status
533534
%% since when we start rabbit for the first time the cluster
@@ -551,7 +552,7 @@ init_db(ClusterNodes, NodeType, CheckOtherNodes) ->
551552
ok = rabbit_table:wait_for_replicated(_Retry = true),
552553
ok = rabbit_table:create_local_copy(NodeType)
553554
end,
554-
ensure_feature_flags_are_in_sync(Nodes),
555+
ensure_feature_flags_are_in_sync(Nodes, NodeIsVirgin),
555556
ensure_schema_integrity(),
556557
rabbit_node_monitor:update_cluster_status(),
557558
ok.
@@ -621,12 +622,12 @@ ensure_mnesia_not_running() ->
621622
throw({error, mnesia_unexpectedly_running})
622623
end.
623624

624-
ensure_feature_flags_are_in_sync(Nodes) ->
625-
case rabbit_feature_flags:sync_feature_flags_with_cluster(Nodes) of
626-
ok ->
627-
ok;
628-
{error, Reason} ->
629-
throw({error, {incompatible_feature_flags, Reason}})
625+
ensure_feature_flags_are_in_sync(Nodes, NodeIsVirgin) ->
626+
Ret = rabbit_feature_flags:sync_feature_flags_with_cluster(
627+
Nodes, NodeIsVirgin),
628+
case Ret of
629+
ok -> ok;
630+
{error, Reason} -> throw({error, {incompatible_feature_flags, Reason}})
630631
end.
631632

632633
ensure_schema_integrity() ->

src/rabbit_plugins.erl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,6 @@ clean_plugins(Plugins) ->
469469
clean_plugin(Plugin, ExpandDir) ->
470470
{ok, Mods} = application:get_key(Plugin, modules),
471471
application:unload(Plugin),
472-
rabbit_feature_flags:initialize_registry(),
473472
[begin
474473
code:soft_purge(Mod),
475474
code:delete(Mod),
@@ -714,4 +713,6 @@ remove_plugins(Plugins) ->
714713
maybe_report_plugin_loading_problems([]) ->
715714
ok;
716715
maybe_report_plugin_loading_problems(Problems) ->
717-
rabbit_log:warning("Problem reading some plugins: ~p~n", [Problems]).
716+
io:format(standard_error,
717+
"Problem reading some plugins: ~p~n",
718+
[Problems]).

0 commit comments

Comments
 (0)