2424
2525-export ([
2626 boot_step /0 ,
27+ conserve_resources /3 ,
2728 parse /2 ,
2829 connect_source /1 ,
2930 connect_dest /1 ,
@@ -76,6 +77,12 @@ boot_step() ->
7677 rabbit_global_counters :init (Labels #{queue_type => rabbit_quorum_queue }),
7778 rabbit_global_counters :init (Labels #{queue_type => rabbit_stream_queue }).
7879
80+ -spec conserve_resources (pid (),
81+ rabbit_alarm :resource_alarm_source (),
82+ rabbit_alarm :resource_alert ()) -> ok .
83+ conserve_resources (Pid , Source , {_ , Conserve , _ }) ->
84+ gen_server :cast (Pid , {conserve_resources , Source , Conserve }).
85+
7986parse (_Name , {source , Source }) ->
8087 Queue = parse_parameter (queue , fun parse_binary /1 ,
8188 proplists :get_value (queue , Source )),
@@ -222,14 +229,17 @@ init_dest(#{name := Name,
222229 dest := #{add_forward_headers := AFH } = Dst } = State ) ->
223230 rabbit_global_counters :publisher_created (? PROTOCOL ),
224231 _TRef = erlang :send_after (1000 , self (), send_confirms_and_nacks ),
232+ Alarms0 = rabbit_alarm :register (self (), {? MODULE , conserve_resources , []}),
233+ Alarms = sets :from_list (Alarms0 ),
225234 case AFH of
226235 true ->
227236 Props = #{<<" x-opt-shovelled-by" >> => rabbit_nodes :cluster_name (),
228237 <<" x-opt-shovel-type" >> => rabbit_data_coercion :to_binary (Type ),
229238 <<" x-opt-shovel-name" >> => rabbit_data_coercion :to_binary (Name )},
230- State #{dest => Dst #{cached_forward_headers => Props }};
239+ State #{dest => Dst #{cached_forward_headers => Props ,
240+ alarms => Alarms }};
231241 false ->
232- State
242+ State #{ dest => Dst #{ alarms => Alarms }}
233243 end .
234244
235245source_uri (_State ) ->
@@ -347,6 +357,19 @@ handle_dest({{'DOWN', #resource{kind = queue,
347357 {eol , QState1 , _QRef } ->
348358 State0 #{dest => Dest #{current => Current #{queue_states => QState1 }}}
349359 end ;
360+ handle_dest ({conserve_resources , Alarm , Conserve }, #{dest := #{alarms := Alarms0 } = Dest } = State0 ) ->
361+ Alarms = case Conserve of
362+ true -> sets :add_element (Alarm , Alarms0 );
363+ false -> sets :del_element (Alarm , Alarms0 )
364+ end ,
365+ State = State0 #{dest => Dest #{alarms => Alarms }},
366+ case {sets :is_empty (Alarms0 ), sets :is_empty (Alarms )} of
367+ {false , true } ->
368+ % % All alarms cleared
369+ forward_pending_delivery (State );
370+ {_ , _ } ->
371+ State
372+ end ;
350373handle_dest (_Msg , State ) ->
351374 State .
352375
@@ -362,7 +385,16 @@ forward(_, _, #{source := #{remaining_unacked := 0}} = State) ->
362385 % % come back. So drop subsequent messages on the floor to be
363386 % % requeued later
364387 State ;
365- forward (Tag , Msg0 , #{dest := #{current := #{queue_states := QState } = Current } = Dest ,
388+ forward (Tag , Msg , State ) ->
389+ case is_blocked (State ) of
390+ true ->
391+ PendingEntry = {Tag , Msg },
392+ add_pending_delivery (PendingEntry , State );
393+ false ->
394+ do_forward (Tag , Msg , State )
395+ end .
396+
397+ do_forward (Tag , Msg0 , #{dest := #{current := #{queue_states := QState } = Current } = Dest ,
366398 ack_mode := AckMode } = State0 ) ->
367399 {Options , #{dest := #{current := Current1 } = Dest1 } = State } =
368400 case AckMode of
@@ -425,10 +457,15 @@ add_routing(Msg0, Dest) ->
425457 RK -> mc :set_annotation (? ANN_ROUTING_KEYS , [RK ], Msg )
426458 end .
427459
428- status (_ ) ->
429- running .
460+ status (State ) ->
461+ case is_blocked (State ) of
462+ true -> blocked ;
463+ false -> running
464+ end .
430465
431- pending_count (_State ) ->
466+ pending_count (#{dest := #{pending_delivery := Pending }}) ->
467+ queue :len (Pending );
468+ pending_count (_ ) ->
432469 0 .
433470
434471% % Internal
@@ -891,3 +928,35 @@ messages_delivered(QName, S0) ->
891928 _ ->
892929 ok
893930 end .
931+
932+ is_blocked (#{dest := #{alarms := Alarms }}) ->
933+ not sets :is_empty (Alarms );
934+ is_blocked (_ ) ->
935+ false .
936+
937+ add_pending_delivery (Elem , State = #{dest := Dest }) ->
938+ Pending = maps :get (pending_delivery , Dest , queue :new ()),
939+ State #{dest => Dest #{pending_delivery => queue :in (Elem , Pending )}}.
940+
941+ pop_pending_delivery (State = #{dest := Dest }) ->
942+ Pending = maps :get (pending_delivery , Dest , queue :new ()),
943+ case queue :out (Pending ) of
944+ {empty , _ } ->
945+ empty ;
946+ {{value , Elem }, Pending2 } ->
947+ {Elem , State #{dest => Dest #{pending_delivery => Pending2 }}}
948+ end .
949+
950+ forward_pending_delivery (State ) ->
951+ case pop_pending_delivery (State ) of
952+ empty ->
953+ State ;
954+ {{Tag , Mc }, S } ->
955+ S2 = do_forward (Tag , Mc , S ),
956+ case is_blocked (S2 ) of
957+ true ->
958+ S2 ;
959+ false ->
960+ forward_pending_delivery (S2 )
961+ end
962+ end .
0 commit comments