24
24
25
25
all () ->
26
26
[
27
- {group , default }
27
+ {group , classic_queue }, { group , quorum_queue }
28
28
].
29
29
30
30
groups () ->
31
31
[
32
- {default , [], [
32
+ {classic_queue , [], [
33
33
all_messages_go_to_one_consumer ,
34
34
fallback_to_another_consumer_when_first_one_is_cancelled ,
35
35
fallback_to_another_consumer_when_exclusive_consumer_channel_is_cancelled ,
36
36
amqp_exclusive_consume_fails_on_exclusive_consumer_queue
37
+ ]},
38
+ {quorum_queue , [], [
39
+ all_messages_go_to_one_consumer ,
40
+ fallback_to_another_consumer_when_first_one_is_cancelled ,
41
+ fallback_to_another_consumer_when_exclusive_consumer_channel_is_cancelled
42
+ % % amqp_exclusive_consume_fails_on_exclusive_consumer_queue % Exclusive consume not implemented in QQ
37
43
]}
38
44
].
39
45
@@ -48,18 +54,37 @@ init_per_suite(Config) ->
48
54
49
55
end_per_suite (Config ) ->
50
56
rabbit_ct_helpers :run_teardown_steps (Config ,
51
- rabbit_ct_client_helpers :teardown_steps () ++
52
- rabbit_ct_broker_helpers :teardown_steps ()).
57
+ rabbit_ct_client_helpers :teardown_steps () ++
58
+ rabbit_ct_broker_helpers :teardown_steps ()).
59
+
60
+ init_per_group (classic_queue , Config ) ->
61
+ [{single_active_consumer_queue_declare ,
62
+ # 'queue.declare' {arguments = [
63
+ {<<" x-single-active-consumer" >>, bool , true },
64
+ {<<" x-queue-type" >>, longstr , <<" classic" >>}
65
+ ],
66
+ auto_delete = true }
67
+ } | Config ];
68
+ init_per_group (quorum_queue , Config ) ->
69
+ [{single_active_consumer_queue_declare ,
70
+ # 'queue.declare' {arguments = [
71
+ {<<" x-single-active-consumer" >>, bool , true },
72
+ {<<" x-queue-type" >>, longstr , <<" quorum" >>}
73
+ ],
74
+ durable = true , exclusive = false , auto_delete = false }
75
+ } | Config ].
76
+
77
+ end_per_group (_ , Config ) ->
78
+ Config .
53
79
54
80
init_per_testcase (Testcase , Config ) ->
55
81
rabbit_ct_helpers :testcase_started (Config , Testcase ).
56
-
57
82
end_per_testcase (Testcase , Config ) ->
58
83
rabbit_ct_helpers :testcase_finished (Config , Testcase ).
59
84
60
85
all_messages_go_to_one_consumer (Config ) ->
61
86
{C , Ch } = connection_and_channel (Config ),
62
- Q = queue_declare (Ch ),
87
+ Q = queue_declare (Ch , Config ),
63
88
NbMessages = 5 ,
64
89
ConsumerPid = spawn (? MODULE , consume , [{self (), {maps :new (), 0 }, NbMessages }]),
65
90
# 'basic.consume_ok' {consumer_tag = CTag1 } =
@@ -85,36 +110,52 @@ all_messages_go_to_one_consumer(Config) ->
85
110
86
111
fallback_to_another_consumer_when_first_one_is_cancelled (Config ) ->
87
112
{C , Ch } = connection_and_channel (Config ),
88
- Q = queue_declare (Ch ),
113
+ Q = queue_declare (Ch , Config ),
89
114
NbMessages = 10 ,
90
115
ConsumerPid = spawn (? MODULE , consume , [{self (), {maps :new (), 0 }, NbMessages }]),
91
116
# 'basic.consume_ok' {consumer_tag = CTag1 } =
92
117
amqp_channel :subscribe (Ch , # 'basic.consume' {queue = Q , no_ack = true }, ConsumerPid ),
93
118
# 'basic.consume_ok' {consumer_tag = CTag2 } =
94
119
amqp_channel :subscribe (Ch , # 'basic.consume' {queue = Q , no_ack = true }, ConsumerPid ),
95
- # 'basic.consume_ok' {consumer_tag = _CTag3 } =
120
+ # 'basic.consume_ok' {consumer_tag = CTag3 } =
96
121
amqp_channel :subscribe (Ch , # 'basic.consume' {queue = Q , no_ack = true }, ConsumerPid ),
97
122
98
123
Publish = # 'basic.publish' {exchange = <<>>, routing_key = Q },
99
124
[amqp_channel :cast (Ch , Publish , # amqp_msg {payload = <<" foobar" >>}) || _X <- lists :seq (1 , NbMessages div 2 )],
100
125
101
- # 'basic.cancel_ok' {} = amqp_channel :call (Ch , # 'basic.cancel' {consumer_tag = CTag1 }),
126
+ {MessagesPerConsumer1 , _ } = wait_for_messages (NbMessages div 2 ),
127
+ FirstActiveConsumerInList = maps :keys (maps :filter (fun (_CTag , MessageCount ) -> MessageCount > 0 end , MessagesPerConsumer1 )),
128
+ ? assertEqual (1 , length (FirstActiveConsumerInList )),
129
+
130
+ FirstActiveConsumer = lists :nth (1 , FirstActiveConsumerInList ),
131
+ # 'basic.cancel_ok' {} = amqp_channel :call (Ch , # 'basic.cancel' {consumer_tag = FirstActiveConsumer }),
132
+
133
+ {cancel_ok , FirstActiveConsumer } = wait_for_cancel_ok (),
102
134
103
135
[amqp_channel :cast (Ch , Publish , # amqp_msg {payload = <<" foobar" >>}) || _X <- lists :seq (NbMessages div 2 + 1 , NbMessages - 1 )],
104
136
105
- # 'basic.cancel_ok' {} = amqp_channel :call (Ch , # 'basic.cancel' {consumer_tag = CTag2 }),
137
+ {MessagesPerConsumer2 , _ } = wait_for_messages (NbMessages div 2 - 1 ),
138
+ SecondActiveConsumerInList = maps :keys (maps :filter (
139
+ fun (CTag , MessageCount ) -> MessageCount > 0 andalso CTag /= FirstActiveConsumer end ,
140
+ MessagesPerConsumer2 )
141
+ ),
142
+ ? assertEqual (1 , length (SecondActiveConsumerInList )),
143
+ SecondActiveConsumer = lists :nth (1 , SecondActiveConsumerInList ),
144
+
145
+ # 'basic.cancel_ok' {} = amqp_channel :call (Ch , # 'basic.cancel' {consumer_tag = SecondActiveConsumer }),
106
146
107
147
amqp_channel :cast (Ch , Publish , # amqp_msg {payload = <<" foobar" >>}),
148
+ wait_for_messages (1 ),
149
+
150
+ LastActiveConsumer = lists :nth (1 , lists :delete (FirstActiveConsumer , lists :delete (SecondActiveConsumer , [CTag1 , CTag2 , CTag3 ]))),
108
151
109
152
receive
110
153
{consumer_done , {MessagesPerConsumer , MessageCount }} ->
111
154
? assertEqual (NbMessages , MessageCount ),
112
155
? assertEqual (3 , maps :size (MessagesPerConsumer )),
113
- ? assertEqual (NbMessages div 2 , maps :get (CTag1 , MessagesPerConsumer )),
114
- Counts = maps :values (MessagesPerConsumer ),
115
- ? assert (lists :member (NbMessages div 2 , Counts )),
116
- ? assert (lists :member (NbMessages div 2 - 1 , Counts )),
117
- ? assert (lists :member (1 , Counts ))
156
+ ? assertEqual (NbMessages div 2 , maps :get (FirstActiveConsumer , MessagesPerConsumer )),
157
+ ? assertEqual (NbMessages div 2 - 1 , maps :get (SecondActiveConsumer , MessagesPerConsumer )),
158
+ ? assertEqual (1 , maps :get (LastActiveConsumer , MessagesPerConsumer ))
118
159
after 1000 ->
119
160
throw (failed )
120
161
end ,
@@ -127,7 +168,7 @@ fallback_to_another_consumer_when_exclusive_consumer_channel_is_cancelled(Config
127
168
{C1 , Ch1 } = connection_and_channel (Config ),
128
169
{C2 , Ch2 } = connection_and_channel (Config ),
129
170
{C3 , Ch3 } = connection_and_channel (Config ),
130
- Q = queue_declare (Ch ),
171
+ Q = queue_declare (Ch , Config ),
131
172
NbMessages = 10 ,
132
173
Consumer1Pid = spawn (? MODULE , consume , [{self (), {maps :new (), 0 }, NbMessages div 2 }]),
133
174
Consumer2Pid = spawn (? MODULE , consume , [{self (), {maps :new (), 0 }, NbMessages div 2 - 1 }]),
@@ -168,7 +209,7 @@ fallback_to_another_consumer_when_exclusive_consumer_channel_is_cancelled(Config
168
209
169
210
amqp_exclusive_consume_fails_on_exclusive_consumer_queue (Config ) ->
170
211
{C , Ch } = connection_and_channel (Config ),
171
- Q = queue_declare (Ch ),
212
+ Q = queue_declare (Ch , Config ),
172
213
? assertExit (
173
214
{{shutdown , {server_initiated_close , 403 , _ }}, _ },
174
215
amqp_channel :call (Ch , # 'basic.consume' {queue = Q , exclusive = true })
@@ -181,9 +222,8 @@ connection_and_channel(Config) ->
181
222
{ok , Ch } = amqp_connection :open_channel (C ),
182
223
{C , Ch }.
183
224
184
- queue_declare (Channel ) ->
185
- Declare = # 'queue.declare' {arguments = [{" x-single-active-consumer" , bool , true }],
186
- auto_delete = true },
225
+ queue_declare (Channel , Config ) ->
226
+ Declare = ? config (single_active_consumer_queue_declare , Config ),
187
227
# 'queue.declare_ok' {queue = Q } = amqp_channel :call (Channel , Declare ),
188
228
Q .
189
229
@@ -198,11 +238,12 @@ consume({Parent, {MessagesPerConsumer, MessageCount}, CountDown}) ->
198
238
{maps :update_with (CTag , fun (V ) -> V + 1 end , MessagesPerConsumer ),
199
239
MessageCount + 1 }};
200
240
{# 'basic.deliver' {consumer_tag = CTag }, _Content } ->
201
- consume ({Parent ,
202
- {maps :update_with (CTag , fun (V ) -> V + 1 end , MessagesPerConsumer ),
203
- MessageCount + 1 },
204
- CountDown - 1 });
205
- # 'basic.cancel_ok' {} ->
241
+ NewState = {maps :update_with (CTag , fun (V ) -> V + 1 end , MessagesPerConsumer ),
242
+ MessageCount + 1 },
243
+ Parent ! {message , NewState },
244
+ consume ({Parent , NewState , CountDown - 1 });
245
+ # 'basic.cancel_ok' {consumer_tag = CTag } ->
246
+ Parent ! {cancel_ok , CTag },
206
247
consume ({Parent , {MessagesPerConsumer , MessageCount }, CountDown });
207
248
_ ->
208
249
consume ({Parent , {MessagesPerConsumer , MessageCount }, CountDown })
@@ -216,7 +257,30 @@ consume_results() ->
216
257
{consumer_done , {MessagesPerConsumer , MessageCount }} ->
217
258
{MessagesPerConsumer , MessageCount };
218
259
{consumer_timeout , {MessagesPerConsumer , MessageCount }} ->
219
- {MessagesPerConsumer , MessageCount }
260
+ {MessagesPerConsumer , MessageCount };
261
+ _ ->
262
+ consume_results ()
220
263
after 1000 ->
221
264
throw (failed )
265
+ end .
266
+
267
+ wait_for_messages (ExpectedCount ) ->
268
+ wait_for_messages (ExpectedCount , {}).
269
+
270
+ wait_for_messages (0 , State ) ->
271
+ State ;
272
+ wait_for_messages (ExpectedCount , _ ) ->
273
+ receive
274
+ {message , {MessagesPerConsumer , MessageCount }} ->
275
+ wait_for_messages (ExpectedCount - 1 , {MessagesPerConsumer , MessageCount })
276
+ after 5000 ->
277
+ throw (message_waiting_timeout )
278
+ end .
279
+
280
+ wait_for_cancel_ok () ->
281
+ receive
282
+ {cancel_ok , CTag } ->
283
+ {cancel_ok , CTag }
284
+ after 5000 ->
285
+ throw (consumer_cancel_ok_timeout )
222
286
end .
0 commit comments