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 ,
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 ()}}.
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 ()},
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.
327348 feature_state / 0 ,
328349 feature_states / 0 ,
329350 stability / 0 ,
351+ require_level / 0 ,
352+ experiment_level / 0 ,
330353 callback_fun_name / 0 ,
331354 callbacks / 0 ,
332355 callback_name / 0 ,
@@ -696,30 +719,38 @@ info() ->
696719info (Options ) when is_map (Options ) ->
697720 rabbit_ff_extra :info (Options ).
698721
699- - spec get_state (feature_name ()) -> enabled | disabled | unavailable .
722+ - spec get_state (feature_name ()) -> enabled |
723+ state_changing |
724+ disabled |
725+ unavailable .
700726% % @doc
701727% % Returns the state of a feature flag.
702728% %
703729% % The possible states are:
704730% % <ul>
705731% % <li>`enabled': the feature flag is enabled.</li>
732+ % % <li>`state_changing': the feature flag is being enabled.</li>
706733% % <li>`disabled': the feature flag is supported by all nodes in the
707734% % cluster but currently disabled.</li>
708735% % <li>`unavailable': the feature flag is unsupported by at least one
709736% % node in the cluster and can not be enabled for now.</li>
710737% % </ul>
711738% %
712739% % @param FeatureName The name of the feature flag to check.
713- % % @returns `enabled', `disabled' or `unavailable'.
740+ % % @returns `enabled', `state_changing', ` disabled' or `unavailable'.
714741
715742get_state (FeatureName ) when is_atom (FeatureName ) ->
716- IsEnabled = is_enabled (FeatureName ),
743+ IsEnabled = is_enabled (FeatureName , non_blocking ),
717744 case IsEnabled of
718- true -> enabled ;
719- false -> case is_supported (FeatureName ) of
720- true -> disabled ;
721- false -> unavailable
722- end
745+ true ->
746+ enabled ;
747+ state_changing ->
748+ state_changing ;
749+ false ->
750+ case is_supported (FeatureName ) of
751+ true -> disabled ;
752+ false -> unavailable
753+ end
723754 end .
724755
725756- spec get_stability
@@ -809,6 +840,45 @@ get_require_level(FeatureProps) when ?IS_DEPRECATION(FeatureProps) ->
809840 _ -> none
810841 end .
811842
843+ - spec get_experiment_level
844+ (FeatureName ) -> ExperimentLevel | undefined when
845+ FeatureName :: feature_name (),
846+ ExperimentLevel :: experiment_level () | none ;
847+ (FeatureProps ) -> ExperimentLevel when
848+ FeatureProps ::
849+ feature_props_extended () |
850+ rabbit_deprecated_features :feature_props_extended (),
851+ ExperimentLevel :: experiment_level () | none .
852+ % % @doc
853+ % % Returns the experimental level of an experimental feature flag.
854+ % %
855+ % % The possible experiment levels are:
856+ % % <ul>
857+ % % <li>`unsupported': the experimental feature flag must not be enabled in
858+ % % production and upgrades with it enabled is unsupported.</li>
859+ % % <li>`supported': the experimental feature flag is not yet stable enough but
860+ % % upgrades are guarantied to be possible. This is returned too if the
861+ % % feature flag is stable or required.</li>
862+ % % </ul>
863+ % %
864+ % % @param FeatureName The name of the feature flag to check.
865+ % % @param FeatureProps A feature flag properties map.
866+ % % @returns `unsupported', `supported', or `undefined' if the given feature
867+ % % flag name doesn't correspond to a known feature flag.
868+
869+ get_experiment_level (FeatureName ) when is_atom (FeatureName ) ->
870+ case rabbit_ff_registry_wrapper :get (FeatureName ) of
871+ undefined -> undefined ;
872+ FeatureProps -> get_experiment_level (FeatureProps )
873+ end ;
874+ get_experiment_level (FeatureProps ) when ? IS_FEATURE_FLAG (FeatureProps ) ->
875+ case get_stability (FeatureProps ) of
876+ experimental -> maps :get (experiment_level , FeatureProps , unsupported );
877+ _ -> supported
878+ end ;
879+ get_experiment_level (FeatureProps ) when ? IS_DEPRECATION (FeatureProps ) ->
880+ supported .
881+
812882% % -------------------------------------------------------------------
813883% % Feature flags registry.
814884% % -------------------------------------------------------------------
@@ -968,6 +1038,7 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) ->
9681038 doc_url ,
9691039 stability ,
9701040 require_level ,
1041+ experiment_level ,
9711042 depends_on ,
9721043 callbacks ],
9731044 ? assertEqual ([], maps :keys (FeatureProps ) -- ValidProps ),
@@ -979,6 +1050,17 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) ->
9791050 ? assert (Stability =:= stable orelse
9801051 Stability =:= experimental orelse
9811052 Stability =:= required ),
1053+ ? assert (Stability =:= experimental orelse
1054+ not maps :is_key (experiment_level , FeatureProps )),
1055+ ? assert (Stability =:= required orelse
1056+ not maps :is_key (require_level , FeatureProps )),
1057+ RequireLevel = maps :get (require_level , FeatureProps , soft ),
1058+ ? assert (RequireLevel =:= hard orelse RequireLevel =:= soft ),
1059+ ExperimentLevel = maps :get (
1060+ experiment_level , FeatureProps ,
1061+ unsupported ),
1062+ ? assert (ExperimentLevel =:= unsupported orelse
1063+ ExperimentLevel =:= supported ),
9821064 ? assertNot (maps :is_key (migration_fun , FeatureProps )),
9831065 ? assertNot (maps :is_key (warning , FeatureProps )),
9841066 case FeatureProps of
0 commit comments