Skip to content

Commit c90d71b

Browse files
committed
Mark classic queue mirroring as deprecated
[Why] Classic queue mirroring will be removed in RabbitMQ 4.0. Quorum queues provide a better safer alternative. Non-replicated classic queues remain supported. [How] Classic queue mirroring is marked as deprecated in the code using the Deprecated features subsystem (based on feature flags). See #7390 for a description of that subsystem. To test RabbitMQ behavior as if the feature was removed, the following configuration setting can be used: deprecated_features.permit.classic_queue_mirroring = false To turn off classic queue mirroring, there must be no classic mirrored queues declared and no HA policy defined. Once classic queue mirroring is turned off, users will not be able to declare HA policies. Trying to do that from the CLI or the management API will be rejected with a warning in the logs. This impacts clustering too: a node with classic queue mirroring turned off will only cluster with another node which has no HA policy or has classic queue mirroring turned off. Note that given the marketing calendar, the deprecated feature will go directly from "permitted by default" to "removed" in RabbitMQ 4.0. It won't go through the gradual deprecation process. V2: Renamed the deprecated feature from `classic_mirrored_queues` to `classic_queue_mirroring` to better reflect the intention. Otherwise it could be unclear is only the mirroring property is deprecated/removed or classic queues entirely.
1 parent 3da9fe5 commit c90d71b

File tree

4 files changed

+131
-65
lines changed

4 files changed

+131
-65
lines changed

deps/rabbit/src/rabbit.erl

-6
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,6 @@
193193
[{description, "core initialized"},
194194
{requires, kernel_ready}]}).
195195

196-
-rabbit_boot_step({deprecate_cmqs,
197-
[{description, "checks whether mirrored queues are disabled"},
198-
{mfa, {rabbit_mirror_queue_misc, prevent_startup_when_mirroring_is_disabled_but_configured, []}},
199-
{requires, [database]},
200-
{enables, [recovery]}]}).
201-
202196
-rabbit_boot_step({recovery,
203197
[{description, "exchange, queue and binding recovery"},
204198
{mfa, {rabbit, recover, []}},

deps/rabbit/src/rabbit_mirror_queue_misc.erl

+68-55
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
-behaviour(rabbit_policy_validator).
1010
-behaviour(rabbit_policy_merge_strategy).
1111

12+
-include_lib("stdlib/include/assert.hrl").
13+
1214
-include("amqqueue.hrl").
1315

1416
-export([remove_from_queue/3, on_vhost_up/1, add_mirrors/3,
@@ -26,7 +28,8 @@
2628

2729
-export([get_replicas/1, transfer_leadership/2, migrate_leadership_to_existing_replica/2]).
2830

29-
-export([prevent_startup_when_mirroring_is_disabled_but_configured/0]).
31+
%% Deprecated feature callback.
32+
-export([are_cmqs_used/1]).
3033

3134
%% for testing only
3235
-export([module/1]).
@@ -35,6 +38,41 @@
3538

3639
-define(HA_NODES_MODULE, rabbit_mirror_queue_mode_nodes).
3740

41+
-rabbit_deprecated_feature(
42+
{classic_queue_mirroring,
43+
#{deprecation_phase => permitted_by_default,
44+
messages =>
45+
#{when_permitted =>
46+
"Classic mirrored queues are deprecated.\n"
47+
"By default, they can still be used for now.\n"
48+
"Their use will not be permitted by default in the next minor"
49+
"RabbitMQ version (if any) and they will be removed from "
50+
"RabbitMQ 4.0.0.\n"
51+
"To continue using classic mirrored queues when they are not "
52+
"permitted by default, set the following parameter in your "
53+
"configuration:\n"
54+
" \"deprecated_features.permit.classic_queue_mirroring = true\"\n"
55+
"To test RabbitMQ as if they were removed, set this in your "
56+
"configuration:\n"
57+
" \"deprecated_features.permit.classic_queue_mirroring = false\"",
58+
59+
when_denied =>
60+
"Classic mirrored queues are deprecated.\n"
61+
"Their use is not permitted per the configuration (overriding the "
62+
"default, which is permitted):\n"
63+
" \"deprecated_features.permit.classic_queue_mirroring = false\"\n"
64+
"Their use will not be permitted by default in the next minor "
65+
"RabbitMQ version (if any) and they will be removed from "
66+
"RabbitMQ 4.0.0.\n"
67+
"To continue using classic mirrored queues when they are not "
68+
"permitted by default, set the following parameter in your "
69+
"configuration:\n"
70+
" \"deprecated_features.permit.classic_queue_mirroring = true\""
71+
},
72+
doc_url => "https://blog.rabbitmq.com/posts/2021/08/4.0-deprecation-announcements/#removal-of-classic-queue-mirroring",
73+
callbacks => #{is_feature_used => {?MODULE, are_cmqs_used}}
74+
}}).
75+
3876
-rabbit_boot_step(
3977
{?MODULE,
4078
[{description, "HA policy validation"},
@@ -730,62 +768,32 @@ maybe_drop_master_after_sync(Q) when ?is_amqqueue(Q) ->
730768

731769
%%----------------------------------------------------------------------------
732770

733-
mirroring_policies() ->
734-
Policies = rabbit_policy:list_as_maps(),
735-
OpPolicies = rabbit_policy:list_op_as_maps(),
736-
IsMirroringPolicy = fun
737-
(#{definition := #{<<"ha-mode">> := _}}) ->
738-
true;
739-
(_) ->
771+
are_cmqs_permitted() ->
772+
FeatureName = classic_queue_mirroring,
773+
rabbit_deprecated_features:is_permitted(FeatureName).
774+
775+
are_cmqs_used(_) ->
776+
try
777+
LocalPolicies = rabbit_policy:list(),
778+
LocalOpPolicies = rabbit_policy:list_op(),
779+
has_ha_policies(LocalPolicies ++ LocalOpPolicies)
780+
catch
781+
exit:{aborted, {no_exists, _}} ->
782+
%% This node is being initialized for the first time. Therefore it
783+
%% must have no policies.
784+
?assert(rabbit_mnesia:is_running()),
740785
false
741-
end,
742-
{lists:filter(IsMirroringPolicy, Policies), lists:filter(IsMirroringPolicy, OpPolicies)}.
743-
744-
report_vhosts_using_mirroring(MirrorPolicies, PolicyType) ->
745-
PerVhost = lists:foldr(
746-
fun (#{vhost := VHost, name := Name}, Acc) ->
747-
maps:update_with(
748-
VHost,
749-
fun (Message) -> <<Name/binary, ", ", Message/binary>> end,
750-
<<"Virtual host ", VHost/binary, " has ", PolicyType/binary,
751-
" policies that configure mirroring: ", Name/binary>>,
752-
Acc)
753-
end,
754-
#{}, MirrorPolicies),
755-
lists:foreach(
756-
fun (Msg) -> rabbit_log:error("~ts", [Msg]) end,
757-
maps:values(PerVhost)).
758-
759-
prevent_startup_when_mirroring_is_disabled_but_configured() ->
760-
case are_cmqs_permitted() of
761-
true ->
762-
ok;
763-
false ->
764-
Error = {error, {failed_to_deny_deprecated_features, [classic_mirrored_queues]}},
765-
case mirroring_policies() of
766-
{[], []} ->
767-
ok;
768-
{Pols, []} ->
769-
report_vhosts_using_mirroring(Pols, <<"user">>),
770-
exit(Error);
771-
{[], OpPols} ->
772-
report_vhosts_using_mirroring(OpPols, <<"operator">>),
773-
exit(Error);
774-
{Pols, OpPols} ->
775-
report_vhosts_using_mirroring(Pols, <<"user">>),
776-
report_vhosts_using_mirroring(OpPols, <<"operator">>),
777-
exit(Error)
778-
end
779786
end.
780787

781-
are_cmqs_permitted() ->
782-
%% FeatureName = classic_mirrored_queues,
783-
%% rabbit_deprecated_features:is_permitted(FeatureName).
784-
case application:get_env(rabbit, permitted_deprecated_features) of
785-
{ok, #{classic_mirrored_queues := false}} ->
786-
false;
787-
_ -> true
788-
end.
788+
has_ha_policies(Policies) ->
789+
lists:any(
790+
fun(Policy) ->
791+
KeyList = proplists:get_value(definition, Policy),
792+
does_policy_configure_cmq(KeyList)
793+
end, Policies).
794+
795+
does_policy_configure_cmq(KeyList) ->
796+
lists:keymember(<<"ha-mode">>, 1, KeyList).
789797

790798
validate_policy(KeyList) ->
791799
Mode = proplists:get_value(<<"ha-mode">>, KeyList, none),
@@ -801,7 +809,12 @@ validate_policy(KeyList) ->
801809
{_, none, none, none, none, none, none} ->
802810
ok;
803811
{false, _, _, _, _, _, _} ->
804-
{error, "Classic queue mirroring is disabled via node configuration", []};
812+
%% If the policy configures classic mirrored queues and this
813+
%% feature is disabled, we consider this policy not valid and deny
814+
%% it.
815+
FeatureName = classic_queue_mirroring,
816+
Warning = rabbit_deprecated_features:get_warning(FeatureName),
817+
{error, "~ts", [Warning]};
805818
{_, none, _, _, _, _, _} ->
806819
{error, "ha-mode must be specified to specify ha-params, "
807820
"ha-sync-mode or ha-promote-on-shutdown", []};

deps/rabbit/test/config_schema_SUITE_data/rabbit.snippets

+2-2
Original file line numberDiff line numberDiff line change
@@ -929,9 +929,9 @@ credential_validator.regexp = ^abc\\d+",
929929
%%
930930

931931
{deprecated_features_cmq,
932-
"deprecated_features.permit.classic_mirrored_queues = false",
932+
"deprecated_features.permit.classic_queue_mirroring = false",
933933
[{rabbit, [
934-
{permit_deprecated_features, #{classic_mirrored_queues => false}}
934+
{permit_deprecated_features, #{classic_queue_mirroring => false}}
935935
]}],
936936
[]},
937937

deps/rabbit/test/rabbitmq_4_0_deprecations_SUITE.erl

+61-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
when_global_qos_is_not_permitted_from_conf/1,
2727

2828
join_when_ram_node_type_is_permitted_by_default/1,
29-
join_when_ram_node_type_is_not_permitted_from_conf/1
29+
join_when_ram_node_type_is_not_permitted_from_conf/1,
30+
31+
set_policy_when_cmq_is_permitted_by_default/1,
32+
set_policy_when_cmq_is_not_permitted_from_conf/1
3033
]).
3134

3235
suite() ->
@@ -45,7 +48,10 @@ groups() ->
4548
when_global_qos_is_not_permitted_from_conf]},
4649
{ram_node_type, [],
4750
[join_when_ram_node_type_is_permitted_by_default,
48-
join_when_ram_node_type_is_not_permitted_from_conf]}
51+
join_when_ram_node_type_is_not_permitted_from_conf]},
52+
{classic_queue_mirroring, [],
53+
[set_policy_when_cmq_is_permitted_by_default,
54+
set_policy_when_cmq_is_not_permitted_from_conf]}
4955
].
5056

5157
%% -------------------------------------------------------------------
@@ -67,6 +73,8 @@ init_per_group(global_qos, Config) ->
6773
init_per_group(ram_node_type, Config) ->
6874
rabbit_ct_helpers:set_config(Config, [{rmq_nodes_count, 2},
6975
{rmq_nodes_clustered, false}]);
76+
init_per_group(classic_queue_mirroring, Config) ->
77+
rabbit_ct_helpers:set_config(Config, {rmq_nodes_count, 1});
7078
init_per_group(_Group, Config) ->
7179
Config.
7280

@@ -87,6 +95,14 @@ init_per_testcase(
8795
{rabbit,
8896
[{permit_deprecated_features, #{ram_node_type => false}}]}),
8997
init_per_testcase1(Testcase, Config1);
98+
init_per_testcase(
99+
set_policy_when_cmq_is_not_permitted_from_conf = Testcase, Config) ->
100+
Config1 = rabbit_ct_helpers:merge_app_env(
101+
Config,
102+
{rabbit,
103+
[{permit_deprecated_features,
104+
#{classic_queue_mirroring => false}}]}),
105+
init_per_testcase1(Testcase, Config1);
90106
init_per_testcase(Testcase, Config) ->
91107
init_per_testcase1(Testcase, Config).
92108

@@ -233,3 +249,46 @@ get_disc_nodes(Config, Node) ->
233249
lists:sort(
234250
rabbit_ct_broker_helpers:rpc(
235251
Config, Node, rabbit_mnesia, cluster_nodes, [disc])).
252+
253+
%% -------------------------------------------------------------------
254+
%% Classic queue mirroring.
255+
%% -------------------------------------------------------------------
256+
257+
set_policy_when_cmq_is_permitted_by_default(Config) ->
258+
?assertEqual(
259+
ok,
260+
rabbit_ct_broker_helpers:set_ha_policy(
261+
Config, 0, <<".*">>, <<"all">>)),
262+
263+
[NodeA] = rabbit_ct_broker_helpers:get_node_configs(
264+
Config, nodename),
265+
266+
%% Change the advanced configuration file to turn off classic queue
267+
%% mirroring.
268+
ConfigFilename0 = rabbit_ct_broker_helpers:get_node_config(
269+
Config, NodeA, erlang_node_config_filename),
270+
ConfigFilename = ConfigFilename0 ++ ".config",
271+
{ok, [ConfigContent0]} = file:consult(ConfigFilename),
272+
ConfigContent1 = rabbit_ct_helpers:merge_app_env_in_erlconf(
273+
ConfigContent0,
274+
{rabbit, [{permit_deprecated_features,
275+
#{classic_queue_mirroring => false}}]}),
276+
ConfigContent2 = lists:flatten(io_lib:format("~p.~n", [ConfigContent1])),
277+
ok = file:write_file(ConfigFilename, ConfigContent2),
278+
?assertEqual({ok, [ConfigContent1]}, file:consult(ConfigFilename)),
279+
280+
%% Restart the node and see if it was correctly converted to a disc node.
281+
ok = rabbit_control_helper:command(stop_app, NodeA),
282+
?assertMatch(
283+
{error, 69,
284+
<<"Error:\n{:rabbit, {{:failed_to_deny_deprecated_features, "
285+
"[:classic_queue_mirroring]}", _/binary>>},
286+
rabbit_control_helper:command(start_app, NodeA)).
287+
288+
set_policy_when_cmq_is_not_permitted_from_conf(Config) ->
289+
?assertError(
290+
{badmatch,
291+
{error_string,
292+
"Validation failed\n\nClassic mirrored queues are deprecated." ++ _}},
293+
rabbit_ct_broker_helpers:set_ha_policy(
294+
Config, 0, <<".*">>, <<"all">>)).

0 commit comments

Comments
 (0)