7979 max_length ,
8080 % % max length in bytes, if configured
8181 max_bytes ,
82+ % % an action to perform if queue is to be over a limit,
83+ % % can be either drop_head (default) or reject_publish
84+ overflow ,
8285 % % when policies change, this version helps queue
8386 % % determine what previously scheduled/set up state to ignore,
8487 % % e.g. message expiration messages from previously set up timers
@@ -158,7 +161,8 @@ init_state(Q) ->
158161 senders = pmon :new (delegate ),
159162 msg_id_to_channel = gb_trees :empty (),
160163 status = running ,
161- args_policy_version = 0 },
164+ args_policy_version = 0 ,
165+ overflow = drop_head },
162166 rabbit_event :init_stats_timer (State , # q .stats_timer ).
163167
164168init_it (Recover , From , State = # q {q = # amqqueue {exclusive_owner = none }}) ->
@@ -259,7 +263,7 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS,
259263 msg_id_to_channel = MTC },
260264 State2 = process_args_policy (State1 ),
261265 State3 = lists :foldl (fun (Delivery , StateN ) ->
262- deliver_or_enqueue (Delivery , true , StateN )
266+ maybe_deliver_or_enqueue (Delivery , true , StateN )
263267 end , State2 , Deliveries ),
264268 notify_decorators (startup , State3 ),
265269 State3 .
@@ -377,6 +381,7 @@ process_args_policy(State = #q{q = Q,
377381 {<<" message-ttl" >>, fun res_min /2 , fun init_ttl /2 },
378382 {<<" max-length" >>, fun res_min /2 , fun init_max_length /2 },
379383 {<<" max-length-bytes" >>, fun res_min /2 , fun init_max_bytes /2 },
384+ {<<" overflow" >>, fun res_arg /2 , fun init_overflow /2 },
380385 {<<" queue-mode" >>, fun res_arg /2 , fun init_queue_mode /2 }],
381386 drop_expired_msgs (
382387 lists :foldl (fun ({Name , Resolve , Fun }, StateN ) ->
@@ -420,6 +425,12 @@ init_max_bytes(MaxBytes, State) ->
420425 {_Dropped , State1 } = maybe_drop_head (State # q {max_bytes = MaxBytes }),
421426 State1 .
422427
428+ init_overflow (undefined , State ) ->
429+ State ;
430+ init_overflow (Overflow , State ) ->
431+ % % TODO maybe drop head
432+ State # q {overflow = binary_to_existing_atom (Overflow , utf8 )}.
433+
423434init_queue_mode (undefined , State ) ->
424435 State ;
425436init_queue_mode (Mode , State = # q {backing_queue = BQ ,
@@ -620,12 +631,22 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid,
620631 State # q {consumers = Consumers })}
621632 end .
622633
634+ maybe_deliver_or_enqueue (Delivery , Delivered , State = # q {overflow = Overflow }) ->
635+ send_mandatory (Delivery ), % % must do this before confirms
636+ case {will_overflow (Delivery , State ), Overflow } of
637+ {true , reject_publish } ->
638+ % % Drop publish and nack to publisher
639+ nack_publish_no_space (Delivery , Delivered , State );
640+ _ ->
641+ % % Enqueue and maybe drop head later
642+ deliver_or_enqueue (Delivery , Delivered , State )
643+ end .
644+
623645deliver_or_enqueue (Delivery = # delivery {message = Message ,
624646 sender = SenderPid ,
625647 flow = Flow },
626648 Delivered , State = # q {backing_queue = BQ ,
627649 backing_queue_state = BQS }) ->
628- send_mandatory (Delivery ), % % must do this before confirms
629650 {Confirm , State1 } = send_or_record_confirm (Delivery , State ),
630651 Props = message_properties (Message , Confirm , State1 ),
631652 {IsDuplicate , BQS1 } = BQ :is_duplicate (Message , BQS ),
@@ -643,6 +664,7 @@ deliver_or_enqueue(Delivery = #delivery{message = Message,
643664 {BQS3 , MTC1 } = discard (Delivery , BQ , BQS2 , MTC ),
644665 State3 # q {backing_queue_state = BQS3 , msg_id_to_channel = MTC1 };
645666 {undelivered , State3 = # q {backing_queue_state = BQS2 }} ->
667+
646668 BQS3 = BQ :publish (Message , Props , Delivered , SenderPid , Flow , BQS2 ),
647669 {Dropped , State4 = # q {backing_queue_state = BQS4 }} =
648670 maybe_drop_head (State3 # q {backing_queue_state = BQS3 }),
@@ -664,7 +686,9 @@ deliver_or_enqueue(Delivery = #delivery{message = Message,
664686maybe_drop_head (State = # q {max_length = undefined ,
665687 max_bytes = undefined }) ->
666688 {false , State };
667- maybe_drop_head (State ) ->
689+ maybe_drop_head (State = # q {overflow = reject_publish }) ->
690+ {false , State };
691+ maybe_drop_head (State = # q {overflow = drop_head }) ->
668692 maybe_drop_head (false , State ).
669693
670694maybe_drop_head (AlreadyDropped , State = # q {backing_queue = BQ ,
@@ -683,6 +707,35 @@ maybe_drop_head(AlreadyDropped, State = #q{backing_queue = BQ,
683707 {AlreadyDropped , State }
684708 end .
685709
710+ nack_publish_no_space (# delivery {confirm = true ,
711+ sender = SenderPid ,
712+ msg_seq_no = MsgSeqNo } = Delivery ,
713+ _Delivered ,
714+ State = # q { backing_queue = BQ ,
715+ backing_queue_state = BQS ,
716+ msg_id_to_channel = MTC }) ->
717+ {BQS1 , MTC1 } = discard (Delivery , BQ , BQS , MTC ),
718+ gen_server2 :cast (SenderPid , {reject_publish , MsgSeqNo , self ()}),
719+ State # q { backing_queue_state = BQS1 , msg_id_to_channel = MTC1 };
720+ nack_publish_no_space (# delivery {confirm = false },
721+ _Delivered , State ) ->
722+ State .
723+
724+ will_overflow (_ , # q {max_length = undefined ,
725+ max_bytes = undefined }) -> false ;
726+ will_overflow (# delivery {message = Message },
727+ # q {max_length = MaxLen ,
728+ max_bytes = MaxBytes ,
729+ backing_queue = BQ ,
730+ backing_queue_state = BQS }) ->
731+ ExpectedQueueLength = BQ :len (BQS ) + 1 ,
732+
733+ # basic_message {content = # content {payload_fragments_rev = PFR }} = Message ,
734+ MessageSize = iolist_size (PFR ),
735+ ExpectedQueueSizeBytes = BQ :info (message_bytes_ready , BQS ) + MessageSize ,
736+
737+ ExpectedQueueLength > MaxLen orelse ExpectedQueueSizeBytes > MaxBytes .
738+
686739over_max_length (# q {max_length = MaxLen ,
687740 max_bytes = MaxBytes ,
688741 backing_queue = BQ ,
@@ -1242,8 +1295,10 @@ handle_cast({run_backing_queue, Mod, Fun},
12421295 State = # q {backing_queue = BQ , backing_queue_state = BQS }) ->
12431296 noreply (State # q {backing_queue_state = BQ :invoke (Mod , Fun , BQS )});
12441297
1245- handle_cast ({deliver , Delivery = # delivery {sender = Sender ,
1246- flow = Flow }, SlaveWhenPublished },
1298+ handle_cast ({deliver ,
1299+ Delivery = # delivery {sender = Sender ,
1300+ flow = Flow },
1301+ SlaveWhenPublished },
12471302 State = # q {senders = Senders }) ->
12481303 Senders1 = case Flow of
12491304 % % In both credit_flow:ack/1 we are acking messages to the channel
@@ -1258,7 +1313,7 @@ handle_cast({deliver, Delivery = #delivery{sender = Sender,
12581313 noflow -> Senders
12591314 end ,
12601315 State1 = State # q {senders = Senders1 },
1261- noreply (deliver_or_enqueue (Delivery , SlaveWhenPublished , State1 ));
1316+ noreply (maybe_deliver_or_enqueue (Delivery , SlaveWhenPublished , State1 ));
12621317% % [0] The second ack is since the channel thought we were a slave at
12631318% % the time it published this message, so it used two credits (see
12641319% % rabbit_amqqueue:deliver/2).
0 commit comments