@@ -30,6 +30,7 @@ all_tests() ->
30
30
amqpl_table_x_header ,
31
31
amqpl_table_x_header_array_of_tbls ,
32
32
amqpl_death_records ,
33
+ is_death_cycle ,
33
34
amqpl_amqp_bin_amqpl ,
34
35
amqpl_cc_amqp_bin_amqpl ,
35
36
amqp_amqpl_amqp_uuid_correlation_id ,
@@ -222,19 +223,91 @@ amqpl_death_records(_Config) ->
222
223
? assertMatch ({_ , array , [{longstr , <<" apple" >>}]}, header (<<" routing-keys" >>, T1 )),
223
224
224
225
225
- % % second dead letter, e.g. a ttl reason returning to source queue
226
+ % % second dead letter, e.g. an expired reason returning to source queue
226
227
227
228
% % record_death uses a timestamp for death record ordering, ensure
228
229
% % it is definitely higher than the last timestamp taken
229
230
timer :sleep (2 ),
230
- Msg2 = mc :record_death (ttl , <<" dl" >>, Msg1 ),
231
+ Msg2 = mc :record_death (expired , <<" dl" >>, Msg1 ),
231
232
232
233
# content {properties = # 'P_basic' {headers = H2 }} = mc :protocol_state (Msg2 ),
233
234
{_ , array , [{table , T2a }, {table , T2b }]} = header (<<" x-death" >>, H2 ),
234
235
? assertMatch ({_ , longstr , <<" dl" >>}, header (<<" queue" >>, T2a )),
235
236
? assertMatch ({_ , longstr , <<" q1" >>}, header (<<" queue" >>, T2b )),
236
237
ok .
237
238
239
+ is_death_cycle (_Config ) ->
240
+ Content = # content {class_id = 60 ,
241
+ properties = # 'P_basic' {headers = []},
242
+ payload_fragments_rev = [<<" data" >>]},
243
+ Msg0 = mc :prepare (store , mc :init (mc_amqpl , Content , annotations ())),
244
+
245
+ % % Test the followig topology:
246
+ % % Q1 --rejected--> Q2 --expired--> Q3 --expired-->
247
+ % % Q1 --rejected--> Q2 --expired--> Q3
248
+
249
+ Msg1 = mc :record_death (rejected , <<" q1" >>, Msg0 ),
250
+ ? assertNot (mc :is_death_cycle (<<" q1" >>, Msg1 ),
251
+ " A queue that dead letters to itself due to rejected is not considered a cycle." ),
252
+ ? assertNot (mc :is_death_cycle (<<" q2" >>, Msg1 )),
253
+ ? assertNot (mc :is_death_cycle (<<" q3" >>, Msg1 )),
254
+
255
+ timer :sleep (3 ),
256
+ Msg2 = mc :record_death (expired , <<" q2" >>, Msg1 ),
257
+ ? assertNot (mc :is_death_cycle (<<" q1" >>, Msg2 )),
258
+ ? assert (mc :is_death_cycle (<<" q2" >>, Msg2 ),
259
+ " A queue that dead letters to itself due to expired is considered a cycle." ),
260
+ ? assertNot (mc :is_death_cycle (<<" q3" >>, Msg2 )),
261
+
262
+ timer :sleep (3 ),
263
+ Msg3 = mc :record_death (expired , <<" q3" >>, Msg2 ),
264
+ ? assertNot (mc :is_death_cycle (<<" q1" >>, Msg3 )),
265
+ ? assert (mc :is_death_cycle (<<" q2" >>, Msg3 )),
266
+ ? assert (mc :is_death_cycle (<<" q3" >>, Msg3 )),
267
+
268
+ timer :sleep (3 ),
269
+ Msg4 = mc :record_death (rejected , <<" q1" >>, Msg3 ),
270
+ ? assertNot (mc :is_death_cycle (<<" q1" >>, Msg4 )),
271
+ ? assertNot (mc :is_death_cycle (<<" q2" >>, Msg4 )),
272
+ ? assertNot (mc :is_death_cycle (<<" q3" >>, Msg4 )),
273
+
274
+ timer :sleep (3 ),
275
+ Msg5 = mc :record_death (expired , <<" q2" >>, Msg4 ),
276
+ ? assertNot (mc :is_death_cycle (<<" q1" >>, Msg5 )),
277
+ ? assert (mc :is_death_cycle (<<" q2" >>, Msg5 )),
278
+ ? assertNot (mc :is_death_cycle (<<" q3" >>, Msg5 )),
279
+
280
+ ? assertEqual ([<<" q1" >>, <<" q2" >>, <<" q3" >>],
281
+ lists :sort (mc :death_queue_names (Msg5 ))),
282
+ ? assertMatch ({{<<" q2" >>, expired },
283
+ # death {exchange = <<" exch" >>,
284
+ routing_keys = [<<" apple" >>],
285
+ count = 2 ,
286
+ anns = #{first_time := FirstTime ,
287
+ last_time := LastTime }}}
288
+ when FirstTime < LastTime , mc :last_death (Msg5 )),
289
+
290
+ # content {properties = # 'P_basic' {headers = H }} = mc :protocol_state (Msg5 ),
291
+ ? assertMatch ({_ , longstr , <<" q1" >>}, header (<<" x-first-death-queue" >>, H )),
292
+ ? assertMatch ({_ , longstr , <<" q2" >>}, header (<<" x-last-death-queue" >>, H )),
293
+ ? assertMatch ({_ , longstr , <<" rejected" >>}, header (<<" x-first-death-reason" >>, H )),
294
+ ? assertMatch ({_ , longstr , <<" expired" >>}, header (<<" x-last-death-reason" >>, H )),
295
+
296
+ % % We expect the array to be ordered by recency.
297
+ {_ , array , [{table , T1 }, {table , T2 }, {table , T3 }]} = header (<<" x-death" >>, H ),
298
+
299
+ ? assertMatch ({_ , longstr , <<" q2" >>}, header (<<" queue" >>, T1 )),
300
+ ? assertMatch ({_ , longstr , <<" expired" >>}, header (<<" reason" >>, T1 )),
301
+ ? assertMatch ({_ , long , 2 }, header (<<" count" >>, T1 )),
302
+
303
+ ? assertMatch ({_ , longstr , <<" q1" >>}, header (<<" queue" >>, T2 )),
304
+ ? assertMatch ({_ , longstr , <<" rejected" >>}, header (<<" reason" >>, T2 )),
305
+ ? assertMatch ({_ , long , 2 }, header (<<" count" >>, T2 )),
306
+
307
+ ? assertMatch ({_ , longstr , <<" q3" >>}, header (<<" queue" >>, T3 )),
308
+ ? assertMatch ({_ , longstr , <<" expired" >>}, header (<<" reason" >>, T3 )),
309
+ ? assertMatch ({_ , long , 1 }, header (<<" count" >>, T3 )).
310
+
238
311
header (K , H ) ->
239
312
rabbit_basic :header (K , H ).
240
313
0 commit comments