Skip to content

Commit 4824fea

Browse files
Merge pull request #9162 from amazon-mq/nonvoters
Add new QQ members as non-voters
2 parents 3501a00 + 4df3080 commit 4824fea

File tree

12 files changed

+254
-69
lines changed

12 files changed

+254
-69
lines changed

deps/rabbit/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,11 @@ rabbitmq_integration_suite(
941941
size = "medium",
942942
)
943943

944+
rabbitmq_suite(
945+
name = "unit_quorum_queue_SUITE",
946+
size = "medium",
947+
)
948+
944949
rabbitmq_integration_suite(
945950
name = "unit_app_management_SUITE",
946951
size = "medium",

deps/rabbit/app.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,6 +1692,15 @@ def test_suite_beam_files(name = "test_suite_beam_files"):
16921692
erlc_opts = "//:test_erlc_opts",
16931693
deps = ["//deps/amqp_client:erlang_app"],
16941694
)
1695+
erlang_bytecode(
1696+
name = "unit_quorum_queue_SUITE_beam_files",
1697+
testonly = True,
1698+
srcs = ["test/unit_quorum_queue_SUITE.erl"],
1699+
outs = ["test/unit_quorum_queue_SUITE.beam"],
1700+
app_name = "rabbit",
1701+
erlc_opts = "//:test_erlc_opts",
1702+
deps = ["//deps/amqp_client:erlang_app"],
1703+
)
16951704
erlang_bytecode(
16961705
name = "unit_app_management_SUITE_beam_files",
16971706
testonly = True,

deps/rabbit/src/rabbit_observer_cli_quorum_queues.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ sheet_body(PrevState) ->
137137
case maps:get(InternalName, RaStates, undefined) of
138138
leader -> "L";
139139
follower -> "F";
140+
promotable -> "f"; %% temporary non-voter
141+
non_voter -> "-"; %% permanent non-voter
140142
_ -> "?"
141143
end,
142144
format_int(proplists:get_value(memory, ProcInfo)),

deps/rabbit/src/rabbit_quorum_queue.erl

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,24 @@
3939
-export([open_files/1]).
4040
-export([peek/2, peek/3]).
4141
-export([add_member/2,
42-
add_member/4]).
42+
add_member/3,
43+
add_member/4,
44+
add_member/5]).
4345
-export([delete_member/3, delete_member/2]).
4446
-export([requeue/3]).
4547
-export([policy_changed/1]).
4648
-export([format_ra_event/3]).
4749
-export([cleanup_data_dir/0]).
4850
-export([shrink_all/1,
49-
grow/4]).
51+
grow/4,
52+
grow/5]).
5053
-export([transfer_leadership/2, get_replicas/1, queue_length/1]).
5154
-export([file_handle_leader_reservation/1,
5255
file_handle_other_reservation/0]).
5356
-export([file_handle_release_reservation/0]).
5457
-export([list_with_minimum_quorum/0,
5558
filter_quorum_critical/1,
56-
filter_quorum_critical/2,
59+
filter_quorum_critical/3,
5760
all_replica_states/0]).
5861
-export([capabilities/0]).
5962
-export([repair_amqqueue_nodes/1,
@@ -84,6 +87,7 @@
8487
-type msg_id() :: non_neg_integer().
8588
-type qmsg() :: {rabbit_types:r('queue'), pid(), msg_id(), boolean(),
8689
mc:state()}.
90+
-type membership() :: voter | non_voter | promotable. %% see ra_membership() in Ra.
8791

8892
-define(RA_SYSTEM, quorum_queues).
8993
-define(RA_WAL_NAME, ra_log_wal).
@@ -384,13 +388,15 @@ become_leader(QName, Name) ->
384388
all_replica_states() ->
385389
Rows0 = ets:tab2list(ra_state),
386390
Rows = lists:map(fun
387-
%% TODO: support other membership types
391+
({K, follower, promotable}) ->
392+
{K, promotable};
393+
({K, follower, non_voter}) ->
394+
{K, non_voter};
388395
({K, S, voter}) ->
389396
{K, S};
390397
(T) ->
391398
T
392399
end, Rows0),
393-
394400
{node(), maps:from_list(Rows)}.
395401

396402
-spec list_with_minimum_quorum() -> [amqqueue:amqqueue()].
@@ -419,20 +425,22 @@ filter_quorum_critical(Queues) ->
419425
ReplicaStates = maps:from_list(
420426
rabbit_misc:append_rpc_all_nodes(rabbit_nodes:list_running(),
421427
?MODULE, all_replica_states, [])),
422-
filter_quorum_critical(Queues, ReplicaStates).
428+
filter_quorum_critical(Queues, ReplicaStates, node()).
423429

424-
-spec filter_quorum_critical([amqqueue:amqqueue()], #{node() => #{atom() => atom()}}) -> [amqqueue:amqqueue()].
430+
-spec filter_quorum_critical([amqqueue:amqqueue()], #{node() => #{atom() => atom()}}, node()) ->
431+
[amqqueue:amqqueue()].
425432

426-
filter_quorum_critical(Queues, ReplicaStates) ->
433+
filter_quorum_critical(Queues, ReplicaStates, Self) ->
427434
lists:filter(fun (Q) ->
428435
MemberNodes = rabbit_amqqueue:get_quorum_nodes(Q),
429436
{Name, _Node} = amqqueue:get_pid(Q),
430437
AllUp = lists:filter(fun (N) ->
431-
{Name, _} = amqqueue:get_pid(Q),
432438
case maps:get(N, ReplicaStates, undefined) of
433439
#{Name := State}
434440
when State =:= follower orelse
435-
State =:= leader ->
441+
State =:= leader orelse
442+
(State =:= promotable andalso N =:= Self) orelse
443+
(State =:= non_voter andalso N =:= Self) ->
436444
true;
437445
_ -> false
438446
end
@@ -1143,7 +1151,7 @@ get_sys_status(Proc) ->
11431151

11441152
end.
11451153

1146-
add_member(VHost, Name, Node, Timeout) ->
1154+
add_member(VHost, Name, Node, Membership, Timeout) when is_binary(VHost) ->
11471155
QName = #resource{virtual_host = VHost, name = Name, kind = queue},
11481156
rabbit_log:debug("Asked to add a replica for queue ~ts on node ~ts", [rabbit_misc:rs(QName), Node]),
11491157
case rabbit_amqqueue:lookup(QName) of
@@ -1161,7 +1169,7 @@ add_member(VHost, Name, Node, Timeout) ->
11611169
rabbit_log:debug("Quorum ~ts already has a replica on node ~ts", [rabbit_misc:rs(QName), Node]),
11621170
ok;
11631171
false ->
1164-
add_member(Q, Node, Timeout)
1172+
add_member(Q, Node, Membership, Timeout)
11651173
end
11661174
end;
11671175
{ok, _Q} ->
@@ -1171,9 +1179,15 @@ add_member(VHost, Name, Node, Timeout) ->
11711179
end.
11721180

11731181
add_member(Q, Node) ->
1174-
add_member(Q, Node, ?ADD_MEMBER_TIMEOUT).
1182+
add_member(Q, Node, promotable).
1183+
1184+
add_member(Q, Node, Membership) ->
1185+
add_member(Q, Node, Membership, ?ADD_MEMBER_TIMEOUT).
11751186

1176-
add_member(Q, Node, Timeout) when ?amqqueue_is_quorum(Q) ->
1187+
add_member(VHost, Name, Node, Timeout) when is_binary(VHost) ->
1188+
%% NOTE needed to pass mixed cluster tests.
1189+
add_member(VHost, Name, Node, promotable, Timeout);
1190+
add_member(Q, Node, Membership, Timeout) when ?amqqueue_is_quorum(Q) ->
11771191
{RaName, _} = amqqueue:get_pid(Q),
11781192
QName = amqqueue:get_name(Q),
11791193
%% TODO parallel calls might crash this, or add a duplicate in quorum_nodes
@@ -1183,7 +1197,7 @@ add_member(Q, Node, Timeout) when ?amqqueue_is_quorum(Q) ->
11831197
?TICK_TIMEOUT),
11841198
SnapshotInterval = application:get_env(rabbit, quorum_snapshot_interval,
11851199
?SNAPSHOT_INTERVAL),
1186-
Conf = make_ra_conf(Q, ServerId, TickTimeout, SnapshotInterval, voter),
1200+
Conf = make_ra_conf(Q, ServerId, TickTimeout, SnapshotInterval, Membership),
11871201
case ra:start_server(?RA_SYSTEM, Conf) of
11881202
ok ->
11891203
ServerIdSpec = maps:with([id, uid, membership], Conf),
@@ -1295,17 +1309,21 @@ shrink_all(Node) ->
12951309
amqqueue:get_type(Q) == ?MODULE,
12961310
lists:member(Node, get_nodes(Q))].
12971311

1298-
-spec grow(node(), binary(), binary(), all | even) ->
1312+
1313+
grow(Node, VhostSpec, QueueSpec, Strategy) ->
1314+
grow(Node, VhostSpec, QueueSpec, Strategy, promotable).
1315+
1316+
-spec grow(node(), binary(), binary(), all | even, membership()) ->
12991317
[{rabbit_amqqueue:name(),
13001318
{ok, pos_integer()} | {error, pos_integer(), term()}}].
1301-
grow(Node, VhostSpec, QueueSpec, Strategy) ->
1319+
grow(Node, VhostSpec, QueueSpec, Strategy, Membership) ->
13021320
Running = rabbit_nodes:list_running(),
13031321
[begin
13041322
Size = length(get_nodes(Q)),
13051323
QName = amqqueue:get_name(Q),
13061324
rabbit_log:info("~ts: adding a new member (replica) on node ~w",
13071325
[rabbit_misc:rs(QName), Node]),
1308-
case add_member(Q, Node, ?ADD_MEMBER_TIMEOUT) of
1326+
case add_member(Q, Node, Membership) of
13091327
ok ->
13101328
{QName, {ok, Size + 1}};
13111329
{error, Err} ->

deps/rabbit/test/quorum_queue_SUITE.erl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,7 +1792,7 @@ add_member_not_running(Config) ->
17921792
declare(Ch, QQ, [{<<"x-queue-type">>, longstr, <<"quorum">>}])),
17931793
?assertEqual({error, node_not_running},
17941794
rpc:call(Server, rabbit_quorum_queue, add_member,
1795-
[<<"/">>, QQ, 'rabbit@burrow', 5000])).
1795+
[<<"/">>, QQ, 'rabbit@burrow', voter, 5000])).
17961796

17971797
add_member_classic(Config) ->
17981798
[Server | _] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
@@ -1801,7 +1801,7 @@ add_member_classic(Config) ->
18011801
?assertEqual({'queue.declare_ok', CQ, 0, 0}, declare(Ch, CQ, [])),
18021802
?assertEqual({error, classic_queue_not_supported},
18031803
rpc:call(Server, rabbit_quorum_queue, add_member,
1804-
[<<"/">>, CQ, Server, 5000])).
1804+
[<<"/">>, CQ, Server, voter, 5000])).
18051805

18061806
add_member_wrong_type(Config) ->
18071807
[Server | _] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
@@ -1811,7 +1811,7 @@ add_member_wrong_type(Config) ->
18111811
declare(Ch, SQ, [{<<"x-queue-type">>, longstr, <<"stream">>}])),
18121812
?assertEqual({error, not_quorum_queue},
18131813
rpc:call(Server, rabbit_quorum_queue, add_member,
1814-
[<<"/">>, SQ, Server, 5000])).
1814+
[<<"/">>, SQ, Server, voter, 5000])).
18151815

18161816
add_member_already_a_member(Config) ->
18171817
[Server | _] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
@@ -1822,14 +1822,14 @@ add_member_already_a_member(Config) ->
18221822
%% idempotent by design
18231823
?assertEqual(ok,
18241824
rpc:call(Server, rabbit_quorum_queue, add_member,
1825-
[<<"/">>, QQ, Server, 5000])).
1825+
[<<"/">>, QQ, Server, voter, 5000])).
18261826

18271827
add_member_not_found(Config) ->
18281828
[Server | _] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
18291829
QQ = ?config(queue_name, Config),
18301830
?assertEqual({error, not_found},
18311831
rpc:call(Server, rabbit_quorum_queue, add_member,
1832-
[<<"/">>, QQ, Server, 5000])).
1832+
[<<"/">>, QQ, Server, voter, 5000])).
18331833

18341834
add_member(Config) ->
18351835
[Server0, Server1] = Servers0 =
@@ -1840,12 +1840,12 @@ add_member(Config) ->
18401840
declare(Ch, QQ, [{<<"x-queue-type">>, longstr, <<"quorum">>}])),
18411841
?assertEqual({error, node_not_running},
18421842
rpc:call(Server0, rabbit_quorum_queue, add_member,
1843-
[<<"/">>, QQ, Server1, 5000])),
1843+
[<<"/">>, QQ, Server1, voter, 5000])),
18441844
ok = rabbit_control_helper:command(stop_app, Server1),
18451845
ok = rabbit_control_helper:command(join_cluster, Server1, [atom_to_list(Server0)], []),
18461846
rabbit_control_helper:command(start_app, Server1),
18471847
?assertEqual(ok, rpc:call(Server1, rabbit_quorum_queue, add_member,
1848-
[<<"/">>, QQ, Server1, 5000])),
1848+
[<<"/">>, QQ, Server1, voter, 5000])),
18491849
Info = rpc:call(Server0, rabbit_quorum_queue, infos,
18501850
[rabbit_misc:r(<<"/">>, queue, QQ)]),
18511851
Servers = lists:sort(Servers0),
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
-module(unit_quorum_queue_SUITE).
2+
3+
-compile(export_all).
4+
5+
all() ->
6+
[
7+
all_replica_states_includes_nonvoters,
8+
filter_quorum_critical_accounts_nonvoters
9+
].
10+
11+
filter_quorum_critical_accounts_nonvoters(_Config) ->
12+
Nodes = [test@leader, test@follower1, test@follower2],
13+
Qs0 = [amqqueue:new(rabbit_misc:r(<<"/">>, queue, <<"q1">>),
14+
{q1, test@leader},
15+
false, false, none, [], undefined, #{}),
16+
amqqueue:new(rabbit_misc:r(<<"/">>, queue, <<"q2">>),
17+
{q2, test@leader},
18+
false, false, none, [], undefined, #{})],
19+
Qs = [Q1, Q2] = lists:map(fun (Q) ->
20+
amqqueue:set_type_state(Q, #{nodes => Nodes})
21+
end, Qs0),
22+
Ss = #{test@leader => #{q1 => leader, q2 => leader},
23+
test@follower1 => #{q1 => promotable, q2 => follower},
24+
test@follower2 => #{q1 => follower, q2 => promotable}},
25+
Qs = rabbit_quorum_queue:filter_quorum_critical(Qs, Ss, test@leader),
26+
[Q2] = rabbit_quorum_queue:filter_quorum_critical(Qs, Ss, test@follower1),
27+
[Q1] = rabbit_quorum_queue:filter_quorum_critical(Qs, Ss, test@follower2),
28+
ok.
29+
30+
all_replica_states_includes_nonvoters(_Config) ->
31+
ets:new(ra_state, [named_table, public, {write_concurrency, true}]),
32+
ets:insert(ra_state, [
33+
{q1, leader, voter},
34+
{q2, follower, voter},
35+
{q3, follower, promotable},
36+
%% pre ra-2.7.0
37+
{q4, leader},
38+
{q5, follower}
39+
]),
40+
{_, #{
41+
q1 := leader,
42+
q2 := follower,
43+
q3 := promotable,
44+
q4 := leader,
45+
q5 := follower
46+
}} = rabbit_quorum_queue:all_replica_states(),
47+
48+
true = ets:delete(ra_state),
49+
ok.

0 commit comments

Comments
 (0)