@@ -105,7 +105,9 @@ groups() ->
105105 force_checkpoint ,
106106 policy_repair ,
107107 gh_12635 ,
108- replica_states
108+ replica_states ,
109+ restart_after_queue_reincarnation ,
110+ no_messages_after_queue_reincarnation
109111 ]
110112 ++ all_tests ()},
111113 {cluster_size_5 , [], [start_queue ,
@@ -2802,15 +2804,21 @@ add_member_wrong_type(Config) ->
28022804 [<<" /" >>, SQ , Server , voter , 5000 ])).
28032805
28042806add_member_already_a_member (Config ) ->
2805- [Server | _ ] = rabbit_ct_broker_helpers :get_node_configs (Config , nodename ),
2807+ [Server , Server2 | _ ] = rabbit_ct_broker_helpers :get_node_configs (Config , nodename ),
28062808 Ch = rabbit_ct_client_helpers :open_channel (Config , Server ),
28072809 QQ = ? config (queue_name , Config ),
28082810 ? assertEqual ({'queue.declare_ok' , QQ , 0 , 0 },
28092811 declare (Ch , QQ , [{<<" x-queue-type" >>, longstr , <<" quorum" >>}])),
2812+ R1 = rpc :call (Server , rabbit_amqqueue , lookup , [{resource , <<" /" >>, queue , QQ }]),
28102813 % % idempotent by design
28112814 ? assertEqual (ok ,
28122815 rpc :call (Server , rabbit_quorum_queue , add_member ,
2813- [<<" /" >>, QQ , Server , voter , 5000 ])).
2816+ [<<" /" >>, QQ , Server , voter , 5000 ])),
2817+ ? assertEqual (R1 , rpc :call (Server , rabbit_amqqueue , lookup , [{resource , <<" /" >>, queue , QQ }])),
2818+ ? assertEqual (ok ,
2819+ rpc :call (Server , rabbit_quorum_queue , add_member ,
2820+ [<<" /" >>, QQ , Server2 , voter , 5000 ])),
2821+ ? assertEqual (R1 , rpc :call (Server , rabbit_amqqueue , lookup , [{resource , <<" /" >>, queue , QQ }])).
28142822
28152823add_member_not_found (Config ) ->
28162824 [Server | _ ] = rabbit_ct_broker_helpers :get_node_configs (Config , nodename ),
@@ -4880,6 +4888,140 @@ replica_states(Config) ->
48804888 end
48814889 end , Result2 ).
48824890
4891+ % Testcase motivated by : https://github.com/rabbitmq/rabbitmq-server/discussions/13131
4892+ restart_after_queue_reincarnation (Config ) ->
4893+ [S1 , S2 , S3 ] = rabbit_ct_broker_helpers :get_node_configs (Config , nodename ),
4894+ Ch = rabbit_ct_client_helpers :open_channel (Config , S1 ),
4895+ QName = <<" QQ" >>,
4896+
4897+ ? assertEqual ({'queue.declare_ok' , QName , 0 , 0 },
4898+ declare (Ch , QName , [{<<" x-queue-type" >>, longstr , <<" quorum" >>}])),
4899+
4900+ [Q ] = rabbit_ct_broker_helpers :rpc (Config , 0 , rabbit_amqqueue , list , []),
4901+ VHost = amqqueue :get_vhost (Q ),
4902+
4903+ MessagesPublished = 1000 ,
4904+ publish_many (Ch , QName , MessagesPublished ),
4905+
4906+ % % Trigger a snapshot by purging the queue.
4907+ rabbit_ct_broker_helpers :rpc (Config , 0 , rabbit_queue_type , purge , [Q ]),
4908+
4909+ % % Stop S3
4910+ rabbit_ct_broker_helpers :mark_as_being_drained (Config , S3 ),
4911+ ? assertEqual (ok , rabbit_control_helper :command (stop_app , S3 )),
4912+
4913+ % % Delete and re-declare queue with the same name.
4914+ rabbit_ct_broker_helpers :rpc (Config , 0 , rabbit_amqqueue , delete , [Q ,false ,false ,<<" dummy_user" >>]),
4915+ ? assertEqual ({'queue.declare_ok' , QName , 0 , 0 },
4916+ declare (Ch , QName , [{<<" x-queue-type" >>, longstr , <<" quorum" >>}])),
4917+
4918+ % Now S3 should have the old queue state, and S1 and S2 a new one.
4919+ St1 = rabbit_ct_broker_helpers :rpc (Config , 0 , rabbit_quorum_queue , status , [VHost , QName ]),
4920+ Status0 = [{proplists :get_value (<<" Node Name" >>, S ), S } || S <- St1 ],
4921+ S3_Status1 = proplists :get_value (S3 , Status0 ),
4922+ Others_Status1 = [V || {_K , V } <- proplists :delete (S3 , Status0 )],
4923+
4924+ S3_LastLogIndex = proplists :get_value (<<" Last Log Index" >>, S3_Status1 ),
4925+ S3_LastWritten = proplists :get_value (<<" Last Written" >>, S3_Status1 ),
4926+ S3_LastApplied = proplists :get_value (<<" Last Applied" >>, S3_Status1 ),
4927+ S3_CommitIndex = proplists :get_value (<<" Commit Index" >>, S3_Status1 ),
4928+ S3_Term = proplists :get_value (<<" Term" >>, S3_Status1 ),
4929+
4930+ ? assertEqual (noproc , proplists :get_value (<<" Raft State" >>, S3_Status1 )),
4931+ ? assertEqual (unknown , proplists :get_value (<<" Membership" >>, S3_Status1 )),
4932+ [begin
4933+ ? assert (S3_LastLogIndex > proplists :get_value (<<" Last Log Index" >>, O )),
4934+ ? assert (S3_LastWritten > proplists :get_value (<<" Last Written" >>, O )),
4935+ ? assert (S3_LastApplied > proplists :get_value (<<" Last Applied" >>, O )),
4936+ ? assert (S3_CommitIndex > proplists :get_value (<<" Commit Index" >>, O )),
4937+ ? assertEqual (S3_Term , proplists :get_value (<<" Term" >>, O ))
4938+ end || O <- Others_Status1 ],
4939+
4940+ % % Bumping term in online nodes
4941+ rabbit_ct_broker_helpers :rpc (Config , 1 , rabbit_quorum_queue , transfer_leadership , [Q , S2 ]),
4942+
4943+ % % Restart S3
4944+ ? assertEqual (ok , rabbit_control_helper :command (start_app , S3 )),
4945+
4946+ timer :sleep (1000 ),
4947+
4948+ % % Now all three nodes should have the new state.
4949+ Status2 = rabbit_ct_broker_helpers :rpc (Config , 0 , rabbit_quorum_queue , status , [VHost , QName ]),
4950+ % They are either leader or follower.
4951+ ? assert (
4952+ lists :all (
4953+ fun (NodeStatus ) ->
4954+ NodeRaftState = proplists :get_value (<<" Raft State" >>, NodeStatus ),
4955+ lists :member (NodeRaftState , [leader , follower ])
4956+ end , Status2 )),
4957+ % Remove "Node Name" and "Raft State" from the status.
4958+ Status3 = [NE1 , NE2 , NE3 ]= [
4959+ begin
4960+ R = proplists :delete (<<" Node Name" >>, NodeEntry ),
4961+ proplists :delete (<<" Raft State" >>, R )
4962+ end || NodeEntry <- Status2 ],
4963+ % Check all other properties have same value on all nodes.
4964+ ct :pal (" Status3: ~tp " , [Status3 ]),
4965+ [
4966+ begin
4967+ ? assertEqual (V , proplists :get_value (K , NE2 )),
4968+ ? assertEqual (V , proplists :get_value (K , NE3 ))
4969+ end || {K , V } <- NE1
4970+ ].
4971+
4972+ % Testcase motivated by : https://github.com/rabbitmq/rabbitmq-server/issues/12366
4973+ no_messages_after_queue_reincarnation (Config ) ->
4974+ [S1 , S2 , S3 ] = rabbit_ct_broker_helpers :get_node_configs (Config , nodename ),
4975+ Ch = rabbit_ct_client_helpers :open_channel (Config , S1 ),
4976+ QName = <<" QQ" >>,
4977+
4978+ ? assertEqual ({'queue.declare_ok' , QName , 0 , 0 },
4979+ declare (Ch , QName , [{<<" x-queue-type" >>, longstr , <<" quorum" >>}])),
4980+
4981+ [Q ] = rabbit_ct_broker_helpers :rpc (Config , 0 , rabbit_amqqueue , list , []),
4982+
4983+ publish (Ch , QName , <<" msg1" >>),
4984+ publish (Ch , QName , <<" msg2" >>),
4985+
4986+ % % Stop S3
4987+ rabbit_ct_broker_helpers :mark_as_being_drained (Config , S3 ),
4988+ ? assertEqual (ok , rabbit_control_helper :command (stop_app , S3 )),
4989+
4990+ qos (Ch , 1 , false ),
4991+ subscribe (Ch , QName , false , <<" tag0" >>, [], 500 ),
4992+ DeliveryTag = receive
4993+ {# 'basic.deliver' {delivery_tag = DT }, # amqp_msg {}} ->
4994+ receive
4995+ {# 'basic.deliver' {consumer_tag = <<" tag0" >>}, # amqp_msg {}} ->
4996+ ct :fail (" did not expect the second one" )
4997+ after 500 ->
4998+ DT
4999+ end
5000+ after 500 ->
5001+ ct :fail (" Expected some delivery, but got none" )
5002+ end ,
5003+
5004+ % % Delete and re-declare queue with the same name.
5005+ rabbit_ct_broker_helpers :rpc (Config , 0 , rabbit_amqqueue , delete , [Q ,false ,false ,<<" dummy_user" >>]),
5006+ ? assertEqual ({'queue.declare_ok' , QName , 0 , 0 },
5007+ declare (Ch , QName , [{<<" x-queue-type" >>, longstr , <<" quorum" >>}])),
5008+
5009+ % % Bumping term in online nodes
5010+ rabbit_ct_broker_helpers :rpc (Config , 1 , rabbit_quorum_queue , transfer_leadership , [Q , S2 ]),
5011+
5012+ % % Restart S3
5013+ ? assertEqual (ok , rabbit_control_helper :command (start_app , S3 )),
5014+
5015+ ok = amqp_channel :cast (Ch , # 'basic.ack' {delivery_tag = DeliveryTag ,
5016+ multiple = false }),
5017+ % % No message should be delivered after reincarnation
5018+ receive
5019+ {# 'basic.deliver' {consumer_tag = <<" tag0" >>}, # amqp_msg {}} ->
5020+ ct :fail (" Expected no deliveries, but got one" )
5021+ after 500 ->
5022+ ok
5023+ end .
5024+
48835025% %----------------------------------------------------------------------------
48845026
48855027same_elements (L1 , L2 )
@@ -4949,7 +5091,10 @@ consume_empty(Ch, Queue, NoAck) ->
49495091subscribe (Ch , Queue , NoAck ) ->
49505092 subscribe (Ch , Queue , NoAck , <<" ctag" >>, []).
49515093
5094+
49525095subscribe (Ch , Queue , NoAck , Tag , Args ) ->
5096+ subscribe (Ch , Queue , NoAck , Tag , Args , ? TIMEOUT ).
5097+ subscribe (Ch , Queue , NoAck , Tag , Args , Timeout ) ->
49535098 amqp_channel :subscribe (Ch , # 'basic.consume' {queue = Queue ,
49545099 no_ack = NoAck ,
49555100 arguments = Args ,
@@ -4958,7 +5103,7 @@ subscribe(Ch, Queue, NoAck, Tag, Args) ->
49585103 receive
49595104 # 'basic.consume_ok' {consumer_tag = Tag } ->
49605105 ok
4961- after ? TIMEOUT ->
5106+ after Timeout ->
49625107 flush (100 ),
49635108 exit (subscribe_timeout )
49645109 end .
0 commit comments