Skip to content

Commit cce74e1

Browse files
authored
Merge pull request #6827 from rabbitmq/mergify/bp/v3.11.x/pr-6682
Feature flags: Add virgin node init support to v2 code path (backport #6682)
2 parents 14c794b + 017955c commit cce74e1

File tree

2 files changed

+150
-74
lines changed

2 files changed

+150
-74
lines changed

deps/rabbit/src/rabbit_feature_flags.erl

Lines changed: 7 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,15 +1826,19 @@ sync_feature_flags_with_cluster(Nodes, NodeIsVirgin) ->
18261826
%% @private
18271827

18281828
sync_feature_flags_with_cluster(Nodes, NodeIsVirgin, Timeout) ->
1829+
Clustered = Nodes =/= [],
18291830
case is_enabled(feature_flags_v2) of
1830-
true -> rabbit_ff_controller:sync_cluster();
1831-
false -> sync_cluster_v1(Nodes, NodeIsVirgin, Timeout)
1831+
true when Clustered -> rabbit_ff_controller:sync_cluster();
1832+
true when NodeIsVirgin -> rabbit_ff_controller:enable_default();
1833+
true -> ok;
1834+
false -> sync_cluster_v1(Nodes, NodeIsVirgin, Timeout)
18321835
end.
18331836

18341837
sync_cluster_v1([], NodeIsVirgin, _) ->
18351838
case NodeIsVirgin of
18361839
true ->
1837-
FeatureNames = get_forced_feature_flag_names(),
1840+
FeatureNames =
1841+
rabbit_ff_controller:get_forced_feature_flag_names(),
18381842
case remote_nodes() of
18391843
[] when FeatureNames =:= undefined ->
18401844
rabbit_log_feature_flags:debug(
@@ -2068,75 +2072,6 @@ do_sync_feature_flags_with_node([FeatureFlag | Rest]) ->
20682072
do_sync_feature_flags_with_node([]) ->
20692073
ok.
20702074

2071-
-spec get_forced_feature_flag_names() -> [feature_name()] | undefined.
2072-
%% @private
2073-
%% @doc
2074-
%% Returns the (possibly empty) list of feature flags the user want
2075-
%% to enable out-of-the-box when starting a node for the first time.
2076-
%%
2077-
%% Without this, the default is to enable all the supported feature
2078-
%% flags.
2079-
%%
2080-
%% There are two ways to specify that list:
2081-
%% <ol>
2082-
%% <li>Using the `$RABBITMQ_FEATURE_FLAGS' environment variable; for
2083-
%% instance `RABBITMQ_FEATURE_FLAGS=quorum_queue,mnevis'.</li>
2084-
%% <li>Using the `forced_feature_flags_on_init' configuration parameter;
2085-
%% for instance
2086-
%% `{rabbit, [{forced_feature_flags_on_init, [quorum_queue, mnevis]}]}'.</li>
2087-
%% </ol>
2088-
%%
2089-
%% The environment variable has precedence over the configuration
2090-
%% parameter.
2091-
2092-
get_forced_feature_flag_names() ->
2093-
Ret = case get_forced_feature_flag_names_from_env() of
2094-
undefined -> get_forced_feature_flag_names_from_config();
2095-
List -> List
2096-
end,
2097-
case Ret of
2098-
undefined -> ok;
2099-
[] -> rabbit_log_feature_flags:info(
2100-
"Feature flags: automatic enablement of feature "
2101-
"flags disabled (i.e. none will be enabled "
2102-
"automatically)");
2103-
_ -> rabbit_log_feature_flags:info(
2104-
"Feature flags: automatic enablement of feature "
2105-
"flags limited to the following list: ~p", [Ret])
2106-
end,
2107-
Ret.
2108-
2109-
-spec get_forced_feature_flag_names_from_env() -> [feature_name()] | undefined.
2110-
%% @private
2111-
2112-
get_forced_feature_flag_names_from_env() ->
2113-
case rabbit_prelaunch:get_context() of
2114-
#{forced_feature_flags_on_init := ForcedFFs}
2115-
when is_list(ForcedFFs) ->
2116-
ForcedFFs;
2117-
_ ->
2118-
undefined
2119-
end.
2120-
2121-
-spec get_forced_feature_flag_names_from_config() -> [feature_name()] | undefined.
2122-
%% @private
2123-
2124-
get_forced_feature_flag_names_from_config() ->
2125-
Value = application:get_env(rabbit,
2126-
forced_feature_flags_on_init,
2127-
undefined),
2128-
case Value of
2129-
undefined ->
2130-
Value;
2131-
_ when is_list(Value) ->
2132-
case lists:all(fun is_atom/1, Value) of
2133-
true -> Value;
2134-
false -> undefined
2135-
end;
2136-
_ ->
2137-
undefined
2138-
end.
2139-
21402075
-spec verify_which_feature_flags_are_actually_enabled() ->
21412076
ok | {error, any()} | no_return().
21422077
%% @private

deps/rabbit/src/rabbit_ff_controller.erl

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,20 @@
3333

3434
-export([is_supported/1, is_supported/2,
3535
enable/1,
36+
enable_default/0,
3637
check_node_compatibility/1,
3738
sync_cluster/0,
38-
refresh_after_app_load/0]).
39+
refresh_after_app_load/0,
40+
get_forced_feature_flag_names/0]).
3941

4042
%% Internal use only.
4143
-export([start/0,
4244
start_link/0,
4345
rpc_call/5,
4446
all_nodes/0,
45-
running_nodes/0]).
47+
running_nodes/0,
48+
collect_inventory_on_nodes/1, collect_inventory_on_nodes/2,
49+
mark_as_enabled_on_nodes/4]).
4650

4751
%% gen_statem callbacks.
4852
-export([callback_mode/0,
@@ -96,6 +100,25 @@ enable(FeatureNames) when is_list(FeatureNames) ->
96100
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
97101
gen_statem:call(?LOCAL_NAME, {enable, FeatureNames}).
98102

103+
enable_default() ->
104+
?LOG_DEBUG(
105+
"Feature flags: configure initial feature flags state",
106+
[],
107+
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
108+
case erlang:whereis(?LOCAL_NAME) of
109+
Pid when is_pid(Pid) ->
110+
%% The function is called while `rabbit' is running.
111+
gen_statem:call(?LOCAL_NAME, enable_default);
112+
undefined ->
113+
%% The function is called while `rabbit' is stopped. We need to
114+
%% start a one-off controller, again to make sure concurrent
115+
%% changes are blocked.
116+
{ok, Pid} = start_link(),
117+
Ret = gen_statem:call(Pid, enable_default),
118+
gen_statem:stop(Pid),
119+
Ret
120+
end.
121+
99122
check_node_compatibility(RemoteNode) ->
100123
ThisNode = node(),
101124
?LOG_DEBUG(
@@ -248,6 +271,8 @@ updating_feature_flag_states(
248271

249272
proceed_with_task({enable, FeatureNames}) ->
250273
enable_task(FeatureNames);
274+
proceed_with_task(enable_default) ->
275+
enable_default_task();
251276
proceed_with_task(sync_cluster) ->
252277
sync_cluster_task();
253278
proceed_with_task(refresh_after_app_load) ->
@@ -451,6 +476,122 @@ enable_task(FeatureNames) ->
451476
{error, missing_clustered_nodes}
452477
end.
453478

479+
enable_default_task() ->
480+
FeatureNames = get_forced_feature_flag_names(),
481+
case FeatureNames of
482+
undefined ->
483+
?LOG_DEBUG(
484+
"Feature flags: starting an unclustered node for the first "
485+
"time: all stable feature flags will be enabled by default",
486+
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
487+
{ok, Inventory} = collect_inventory_on_nodes([node()]),
488+
#{feature_flags := FeatureFlags} = Inventory,
489+
StableFeatureNames =
490+
maps:fold(
491+
fun
492+
(FeatureName, #{stability := stable}, Acc) ->
493+
[FeatureName | Acc];
494+
(_FeatureName, _FeatureProps, Acc) ->
495+
Acc
496+
end, [], FeatureFlags),
497+
enable_many(Inventory, StableFeatureNames);
498+
[] ->
499+
?LOG_DEBUG(
500+
"Feature flags: starting an unclustered node for the first "
501+
"time: all feature flags are forcibly left disabled from "
502+
"the $RABBITMQ_FEATURE_FLAGS environment variable",
503+
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
504+
ok;
505+
_ ->
506+
?LOG_DEBUG(
507+
"Feature flags: starting an unclustered node for the first "
508+
"time: only the following feature flags specified in the "
509+
"$RABBITMQ_FEATURE_FLAGS environment variable will be enabled: "
510+
"~tp",
511+
[FeatureNames],
512+
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
513+
{ok, Inventory} = collect_inventory_on_nodes([node()]),
514+
enable_many(Inventory, FeatureNames)
515+
end.
516+
517+
-spec get_forced_feature_flag_names() -> Ret when
518+
Ret :: FeatureNames | undefined,
519+
FeatureNames :: [rabbit_feature_flags:feature_name()].
520+
%% @doc Returns the (possibly empty) list of feature flags the user wants to
521+
%% enable out-of-the-box when starting a node for the first time.
522+
%%
523+
%% Without this, the default is to enable all the supported stable feature
524+
%% flags.
525+
%%
526+
%% There are two ways to specify that list:
527+
%% <ol>
528+
%% <li>Using the `$RABBITMQ_FEATURE_FLAGS' environment variable; for
529+
%% instance `RABBITMQ_FEATURE_FLAGS=quorum_queue,mnevis'.</li>
530+
%% <li>Using the `forced_feature_flags_on_init' configuration parameter;
531+
%% for instance
532+
%% `{rabbit, [{forced_feature_flags_on_init, [quorum_queue, mnevis]}]}'.</li>
533+
%% </ol>
534+
%%
535+
%% The environment variable has precedence over the configuration parameter.
536+
%%
537+
%% @private
538+
539+
get_forced_feature_flag_names() ->
540+
Ret = case get_forced_feature_flag_names_from_env() of
541+
undefined -> get_forced_feature_flag_names_from_config();
542+
List -> List
543+
end,
544+
case Ret of
545+
undefined ->
546+
ok;
547+
[] ->
548+
?LOG_INFO(
549+
"Feature flags: automatic enablement of feature flags "
550+
"disabled (i.e. none will be enabled automatically)",
551+
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS});
552+
_ ->
553+
?LOG_INFO(
554+
"Feature flags: automatic enablement of feature flags "
555+
"limited to the following list: ~tp",
556+
[Ret],
557+
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS})
558+
end,
559+
Ret.
560+
561+
-spec get_forced_feature_flag_names_from_env() -> Ret when
562+
Ret :: FeatureNames | undefined,
563+
FeatureNames :: [rabbit_feature_flags:feature_name()].
564+
%% @private
565+
566+
get_forced_feature_flag_names_from_env() ->
567+
case rabbit_prelaunch:get_context() of
568+
#{forced_feature_flags_on_init := ForcedFFs}
569+
when is_list(ForcedFFs) ->
570+
ForcedFFs;
571+
_ ->
572+
undefined
573+
end.
574+
575+
-spec get_forced_feature_flag_names_from_config() -> Ret when
576+
Ret :: FeatureNames | undefined,
577+
FeatureNames :: [rabbit_feature_flags:feature_name()].
578+
%% @private
579+
580+
get_forced_feature_flag_names_from_config() ->
581+
Value = application:get_env(
582+
rabbit, forced_feature_flags_on_init, undefined),
583+
case Value of
584+
undefined ->
585+
Value;
586+
_ when is_list(Value) ->
587+
case lists:all(fun is_atom/1, Value) of
588+
true -> Value;
589+
false -> undefined
590+
end;
591+
_ ->
592+
undefined
593+
end.
594+
454595
-spec sync_cluster_task() -> Ret when
455596
Ret :: ok | {error, Reason},
456597
Reason :: term().

0 commit comments

Comments
 (0)