Skip to content

Commit 14e4d53

Browse files
committed
rabbit_feature_flags: Declare if an experimental feature flag is supported or not
[Why] Durint the development of Khepri, it was difficult to communicate that it was unsupported in RabbitMQ 3.13.x but was then supported in 4.0.x even though it was still experimental. [How] The feature flag definition now exposes that support level in a now attribute called `experiment_level`. It can be `unsupported` or `supported`. We can use this now attribute in the CLI or the web UI to convey the level of support to the end user. In the future, we could imagine that an experimental feature flag becomes abandoned, where upgraded from a node that has it enabled to a version that marks the feature flag as abandoned is not possible.
1 parent b9328fe commit 14e4d53

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

deps/rabbit/src/rabbit_core_ff.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,10 @@
146146

147147
-rabbit_feature_flag(
148148
{khepri_db,
149-
#{desc => "New Raft-based metadata store. Fully supported as of RabbitMQ 4.0",
149+
#{desc => "New Raft-based metadata store.",
150150
doc_url => "https://www.rabbitmq.com/docs/next/metadata-store",
151151
stability => experimental,
152+
experiment_level => supported,
152153
depends_on => [feature_flags_v2,
153154
direct_exchange_routing_v2,
154155
maintenance_mode_status,

deps/rabbit/src/rabbit_feature_flags.erl

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
get_state/1,
107107
get_stability/1,
108108
get_require_level/1,
109+
get_experiment_level/1,
109110
check_node_compatibility/1, check_node_compatibility/2,
110111
sync_feature_flags_with_cluster/2,
111112
refresh_feature_flags_after_app_load/0,
@@ -149,6 +150,7 @@
149150
doc_url => string(),
150151
stability => stability(),
151152
require_level => require_level(),
153+
experiment_level => experiment_level(),
152154
depends_on => [feature_name()],
153155
callbacks =>
154156
#{callback_name() => callback_fun_name()}}.
@@ -186,6 +188,7 @@
186188
doc_url => string(),
187189
stability => stability(),
188190
require_level => require_level(),
191+
experiment_level => experiment_level(),
189192
depends_on => [feature_name()],
190193
callbacks =>
191194
#{callback_name() => callback_fun_name()},
@@ -219,6 +222,24 @@
219222
%% A soft required feature flag will be automatically enabled when a RabbitMQ
220223
%% node is upgraded to a version where it is required.
221224

225+
-type experiment_level() :: unsupported | supported.
226+
%% The level of support of an experimental feature flag.
227+
%%
228+
%% At first, an experimental feature flag is offered to give a chance to users
229+
%% to try it and give feedback as part of the design and development of the
230+
%% feature. At this stage, it is unsupported: it must not be enabled in a
231+
%% production environment and upgrade to a later version of RabbitMQ while
232+
%% this experimental feature flag is enabled is not supported.
233+
%%
234+
%% Then, the experimental feature flag becomes supported. At this point, it is
235+
%% stable enough that upgrading is guarantied and help will be provided.
236+
%% However it is not mature enough to be marked as stable (which would make it
237+
%% enabled by default in a new deployment or when running `rabbitmqctl
238+
%% enable_feature_flag all'.
239+
%%
240+
%% The next step is to change its stability to `stable'. Once done, the
241+
%% `experiment_level()' field is irrelevant.
242+
222243
-type callback_fun_name() :: {Module :: module(), Function :: atom()}.
223244
%% The name of the module and function to call when changing the state of
224245
%% the feature flag.
@@ -809,6 +830,45 @@ get_require_level(FeatureProps) when ?IS_DEPRECATION(FeatureProps) ->
809830
_ -> none
810831
end.
811832

833+
-spec get_experiment_level
834+
(FeatureName) -> ExperimentLevel | undefined when
835+
FeatureName :: feature_name(),
836+
ExperimentLevel :: experiment_level() | none;
837+
(FeatureProps) -> ExperimentLevel when
838+
FeatureProps ::
839+
feature_props_extended() |
840+
rabbit_deprecated_features:feature_props_extended(),
841+
ExperimentLevel :: experiment_level() | none.
842+
%% @doc
843+
%% Returns the experimental level of an experimental feature flag.
844+
%%
845+
%% The possible experiment levels are:
846+
%% <ul>
847+
%% <li>`unsupported': the experimental feature flag must not be enabled in
848+
%% production and upgrades with it enabled is unsupported.</li>
849+
%% <li>`supported': the experimental feature flag is not yet stable enough but
850+
%% upgrades are guarantied to be possible. This is returned too if the
851+
%% feature flag is stable or required.</li>
852+
%% </ul>
853+
%%
854+
%% @param FeatureName The name of the feature flag to check.
855+
%% @param FeatureProps A feature flag properties map.
856+
%% @returns `unsupported', `supported', or `undefined' if the given feature
857+
%% flag name doesn't correspond to a known feature flag.
858+
859+
get_experiment_level(FeatureName) when is_atom(FeatureName) ->
860+
case rabbit_ff_registry_wrapper:get(FeatureName) of
861+
undefined -> undefined;
862+
FeatureProps -> get_experiment_level(FeatureProps)
863+
end;
864+
get_experiment_level(FeatureProps) when ?IS_FEATURE_FLAG(FeatureProps) ->
865+
case get_stability(FeatureProps) of
866+
experimental -> maps:get(experiment_level, FeatureProps, unsupported);
867+
_ -> supported
868+
end;
869+
get_experiment_level(FeatureProps) when ?IS_DEPRECATION(FeatureProps) ->
870+
supported.
871+
812872
%% -------------------------------------------------------------------
813873
%% Feature flags registry.
814874
%% -------------------------------------------------------------------
@@ -968,6 +1028,7 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) ->
9681028
doc_url,
9691029
stability,
9701030
require_level,
1031+
experiment_level,
9711032
depends_on,
9721033
callbacks],
9731034
?assertEqual([], maps:keys(FeatureProps) -- ValidProps),
@@ -979,6 +1040,17 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) ->
9791040
?assert(Stability =:= stable orelse
9801041
Stability =:= experimental orelse
9811042
Stability =:= required),
1043+
?assert(Stability =:= experimental orelse
1044+
not maps:is_key(experiment_level, FeatureProps)),
1045+
?assert(Stability =:= required orelse
1046+
not maps:is_key(require_level, FeatureProps)),
1047+
RequireLevel = maps:get(require_level, FeatureProps, soft),
1048+
?assert(RequireLevel =:= hard orelse RequireLevel =:= soft),
1049+
ExperimentLevel = maps:get(
1050+
experiment_level, FeatureProps,
1051+
unsupported),
1052+
?assert(ExperimentLevel =:= unsupported orelse
1053+
ExperimentLevel =:= supported),
9821054
?assertNot(maps:is_key(migration_fun, FeatureProps)),
9831055
?assertNot(maps:is_key(warning, FeatureProps)),
9841056
case FeatureProps of

0 commit comments

Comments
 (0)