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,8 +457,11 @@ 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
431466pending_count (_State ) ->
432467 0 .
@@ -891,3 +926,33 @@ messages_delivered(QName, S0) ->
891926 _ ->
892927 ok
893928 end .
929+
930+ is_blocked (#{dest := #{alarms := Alarms }}) ->
931+ not sets :is_empty (Alarms ).
932+
933+ add_pending_delivery (Elem , State = #{dest := Dest }) ->
934+ Pending = maps :get (pending_delivery , Dest , queue :new ()),
935+ State #{dest => Dest #{pending_delivery => queue :in (Elem , Pending )}}.
936+
937+ pop_pending_delivery (State = #{dest := Dest }) ->
938+ Pending = maps :get (pending_delivery , Dest , queue :new ()),
939+ case queue :out (Pending ) of
940+ {empty , _ } ->
941+ empty ;
942+ {{value , Elem }, Pending2 } ->
943+ {Elem , State #{dest => Dest #{pending_delivery => Pending2 }}}
944+ end .
945+
946+ forward_pending_delivery (State ) ->
947+ case pop_pending_delivery (State ) of
948+ empty ->
949+ State ;
950+ {{Tag , Mc }, S } ->
951+ S2 = do_forward (Tag , Mc , S ),
952+ case is_blocked (S2 ) of
953+ true ->
954+ S2 ;
955+ false ->
956+ forward_pending_delivery (S2 )
957+ end
958+ end .
0 commit comments