Skip to content

Commit 84f6d1b

Browse files
committed
Ensure to only grow QQs when all existing members are in 'voter' status
1 parent 0ed90fe commit 84f6d1b

File tree

4 files changed

+86
-18
lines changed

4 files changed

+86
-18
lines changed

deps/rabbit/src/rabbit_quorum_queue.erl

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,19 +1609,43 @@ grow(QuorumClusterSize, _VhostSpec, _QueueSpec, _Strategy, _Membership)
16091609
{error, bad_quorum_cluster_size}.
16101610

16111611
maybe_grow(Q, Node, Membership, Size) ->
1612+
QNodes = get_nodes(Q),
1613+
maybe_grow(Q, Node, Membership, Size, QNodes).
1614+
1615+
maybe_grow(Q, Node, Membership, Size, QNodes) ->
16121616
QName = amqqueue:get_name(Q),
1613-
?LOG_INFO("~ts: adding a new member (replica) on node ~w",
1614-
[rabbit_misc:rs(QName), Node]),
1615-
case add_member(Q, Node, Membership) of
1616-
ok ->
1617-
{QName, {ok, Size + 1}};
1618-
{error, Err} ->
1617+
{ok, RaName} = qname_to_internal_name(QName),
1618+
case check_all_memberships(RaName, QNodes, voter) of
1619+
true ->
1620+
?LOG_INFO("~ts: adding a new member (replica) on node ~w",
1621+
[rabbit_misc:rs(QName), Node]),
1622+
case add_member(Q, Node, Membership) of
1623+
ok ->
1624+
{QName, {ok, Size + 1}};
1625+
{error, Err} ->
1626+
?LOG_WARNING(
1627+
"~ts: failed to add member (replica) on node ~w, error: ~w",
1628+
[rabbit_misc:rs(QName), Node, Err]),
1629+
{QName, {error, Size, Err}}
1630+
end;
1631+
false ->
1632+
Err = {error, non_voters_found},
16191633
?LOG_WARNING(
1620-
"~ts: failed to add member (replica) on node ~w, error: ~w",
1621-
[rabbit_misc:rs(QName), Node, Err]),
1634+
"~ts: failed to add member (replica) on node ~w, error: ~w",
1635+
[rabbit_misc:rs(QName), Node, Err]),
16221636
{QName, {error, Size, Err}}
16231637
end.
16241638

1639+
check_all_memberships(RaName, QNodes, CompareMembership) ->
1640+
case rpc:multicall(QNodes, ets, lookup, [ra_state, RaName]) of
1641+
{Result, []} ->
1642+
lists:all(
1643+
fun(M) -> M == CompareMembership end,
1644+
[Membership || [{_RaName, _RaState, Membership}] <- Result]);
1645+
_ ->
1646+
false
1647+
end.
1648+
16251649
-spec transfer_leadership(amqqueue:amqqueue(), node()) -> {migrated, node()} | {not_migrated, atom()}.
16261650
transfer_leadership(Q, Destination) ->
16271651
{RaName, _} = Pid = amqqueue:get_pid(Q),

deps/rabbit/test/quorum_queue_SUITE.erl

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,7 +1792,7 @@ dont_leak_file_handles(Config) ->
17921792
ok.
17931793

17941794
grow_queue(Config) ->
1795-
[Server0, Server1, _Server2, _Server3, _Server4] =
1795+
[Server0, Server1, Server2, _Server3, _Server4] =
17961796
rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
17971797

17981798
Ch = rabbit_ct_client_helpers:open_channel(Config, Server0),
@@ -1825,42 +1825,72 @@ grow_queue(Config) ->
18251825

18261826
%% grow queues to node 'Server1'
18271827
TargetClusterSize_2 = 2,
1828-
rpc:call(Server0, rabbit_quorum_queue, grow, [Server1, <<"/">>, <<".*">>, all]),
1828+
Result1 = rpc:call(Server0, rabbit_quorum_queue, grow, [Server1, <<"/">>, <<".*">>, all]),
1829+
%% [{{resource,<<"/">>,queue,<<"grow_queue">>},{ok,2}},
1830+
%% {{resource,<<"/">>,queue,<<"grow_queue_alt">>},{ok,2}},...]
1831+
?assert(lists:all(fun({_, {R, _}}) -> R =:= ok end, Result1)),
18291832
assert_grown_queues(QQs, Server0, TargetClusterSize_2, MsgCount),
18301833

18311834
%% grow queues to quorum cluster size '2' has no effect
1832-
rpc:call(Server0, rabbit_quorum_queue, grow, [TargetClusterSize_2, <<"/">>, <<".*">>, all]),
1835+
Result2 = rpc:call(Server0, rabbit_quorum_queue, grow, [TargetClusterSize_2, <<"/">>, <<".*">>, all]),
1836+
?assertEqual([], Result2),
18331837
assert_grown_queues(QQs, Server0, TargetClusterSize_2, MsgCount),
18341838

18351839
%% grow queues to quorum cluster size '3'
18361840
TargetClusterSize_3 = 3,
1837-
rpc:call(Server0, rabbit_quorum_queue, grow, [TargetClusterSize_3, <<"/">>, <<".*">>, all]),
1841+
Result3 = rpc:call(Server0, rabbit_quorum_queue, grow, [TargetClusterSize_3, <<"/">>, <<".*">>, all, voter]),
1842+
?assert(lists:all(fun({_, {R, _}}) -> R =:= ok end, Result3)),
18381843
assert_grown_queues(QQs, Server0, TargetClusterSize_3, MsgCount),
18391844

18401845
%% grow queues to quorum cluster size '5'
18411846
TargetClusterSize_5 = 5,
1842-
rpc:call(Server0, rabbit_quorum_queue, grow, [TargetClusterSize_5, <<"/">>, <<".*">>, all]),
1847+
Result4 = rpc:call(Server0, rabbit_quorum_queue, grow, [TargetClusterSize_5, <<"/">>, <<".*">>, all, voter]),
1848+
?assert(lists:all(fun({_, {R, _}}) -> R =:= ok end, Result4)),
18431849
assert_grown_queues(QQs, Server0, TargetClusterSize_5, MsgCount),
18441850

1845-
%% shrink all queues again
1851+
%% shrink all queues again down to 1 member
18461852
rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_quorum_queue,
18471853
force_all_queues_shrink_member_to_current_member, []),
18481854
assert_grown_queues(QQs, Server0, TargetClusterSize_1, MsgCount),
18491855

18501856
%% grow queues to quorum cluster size > '5' (limit = 5).
18511857
TargetClusterSize_10 = 10,
1852-
rpc:call(Server0, rabbit_quorum_queue, grow, [TargetClusterSize_10, <<"/">>, <<".*">>, all]),
1858+
Result5 = rpc:call(Server0, rabbit_quorum_queue, grow, [TargetClusterSize_10, <<"/">>, <<".*">>, all]),
1859+
?assert(lists:all(fun({_, {R, _}}) -> R =:= ok end, Result5)),
18531860
assert_grown_queues(QQs, Server0, TargetClusterSize_5, MsgCount),
18541861

1855-
%% shrink all queues again
1862+
%% shrink all queues again down to 1 member
18561863
rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_quorum_queue,
18571864
force_all_queues_shrink_member_to_current_member, []),
18581865
assert_grown_queues(QQs, Server0, TargetClusterSize_1, MsgCount),
18591866

18601867
%% attempt to grow queues to quorum cluster size < '0'.
18611868
BadTargetClusterSize = -5,
18621869
?assertEqual({error, bad_quorum_cluster_size},
1863-
rpc:call(Server0, rabbit_quorum_queue, grow, [BadTargetClusterSize, <<"/">>, <<".*">>, all])).
1870+
rpc:call(Server0, rabbit_quorum_queue, grow, [BadTargetClusterSize, <<"/">>, <<".*">>, all])),
1871+
1872+
%% shrink all queues again down to 1 member
1873+
rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_quorum_queue,
1874+
force_all_queues_shrink_member_to_current_member, []),
1875+
assert_grown_queues(QQs, Server0, TargetClusterSize_1, MsgCount),
1876+
1877+
%% grow queues to node 'Server1': non_voter
1878+
rpc:call(Server0, rabbit_quorum_queue, grow, [Server1, <<"/">>, <<".*">>, all, non_voter]),
1879+
assert_grown_queues(QQs, Server0, TargetClusterSize_2, MsgCount),
1880+
1881+
%% grow queues to node 'Server2': fail, non_voters found
1882+
Result6 = rpc:call(Server0, rabbit_quorum_queue, grow, [Server2, <<"/">>, <<".*">>, all, voter]),
1883+
%% [{{resource,<<"/">>,queue,<<"grow_queue">>},{error, 2, {error, non_voters_found}},
1884+
%% {{resource,<<"/">>,queue,<<"grow_queue_alt">>},{error, 2, {error, non_voters_found}},...]
1885+
?assert(lists:all(
1886+
fun({_, Err}) -> Err =:= {error, TargetClusterSize_2, {error, non_voters_found}} end, Result6)),
1887+
assert_grown_queues(QQs, Server0, TargetClusterSize_2, MsgCount),
1888+
1889+
%% grow queues to target quorum cluster size '5': fail, non_voters found
1890+
Result7 = rpc:call(Server0, rabbit_quorum_queue, grow, [TargetClusterSize_5, <<"/">>, <<".*">>, all]),
1891+
?assert(lists:all(
1892+
fun({_, Err}) -> Err =:= {error, TargetClusterSize_2, {error, non_voters_found}} end, Result7)),
1893+
assert_grown_queues(QQs, Server0, TargetClusterSize_2, MsgCount).
18641894

18651895
assert_grown_queues(Qs, Node, TargetClusterSize, MsgCount) ->
18661896
[begin

deps/rabbitmq_cli/lib/rabbitmq/cli/queues/commands/grow_command.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ defmodule RabbitMQ.CLI.Queues.Commands.GrowCommand do
4444
{:validation_failure, "target quorum cluster size '#{n}' must be greater than 0."}
4545
end
4646

47+
def validate([n, _], %{membership: m})
48+
when (is_integer(n) and not (m == "voter" or m == "promotable")) do
49+
{:validation_failure, "voter status '#{m}' must be 'voter' or 'promotable' to grow to target quorum cluster size '#{n}'."}
50+
end
51+
4752
def validate(_, %{membership: m})
4853
when not (m == "promotable" or
4954
m == "non_voter" or

deps/rabbitmq_cli/test/queues/grow_command_test.exs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,14 @@ defmodule RabbitMQ.CLI.Queues.Commands.GrowCommandTest do
8282
{:validation_failure, "voter status 'banana' is not recognised."}
8383
end
8484

85-
test "validate: when target quorum cluster size greater than zero, returns a success" do
85+
test "validate: when target quorum cluster size greater than zero and membership is voter, returns a success" do
8686
assert @command.validate([7, "all"], %{membership: "voter", queue_pattern: "qq.*"}) == :ok
8787
end
8888

89+
test "validate: when target quorum cluster size greater than zero and membership is promotable, returns a success" do
90+
assert @command.validate([5, "all"], %{membership: "promotable", queue_pattern: "qq.*"}) == :ok
91+
end
92+
8993
test "validate: when target quorum cluster size is zero, returns failure" do
9094
assert @command.validate([0, "all"], %{membership: "voter", queue_pattern: "qq.*"}) ==
9195
{:validation_failure, "target quorum cluster size '0' must be greater than 0."}
@@ -96,6 +100,11 @@ defmodule RabbitMQ.CLI.Queues.Commands.GrowCommandTest do
96100
{:validation_failure, "target quorum cluster size '-1' must be greater than 0."}
97101
end
98102

103+
test "validate: when target quorum cluster size is provided and membership is not voter, returns failure" do
104+
assert @command.validate([5, "all"], %{membership: "non_voter", queue_pattern: "qq.*"}) ==
105+
{:validation_failure, "voter status 'non_voter' must be 'voter' or 'promotable' to grow to target quorum cluster size '5'."}
106+
end
107+
99108
@tag test_timeout: 3000
100109
test "run: targeting an unreachable node throws a badrpc when growing to a target node", context do
101110
assert match?(

0 commit comments

Comments
 (0)