203203 delayed_op / 0 ]).
204204
205205-spec init (config ()) -> state ().
206- init (#{name := Name ,
207- queue_resource := Resource } = Conf ) ->
208- update_config (Conf , #? STATE {cfg = # cfg {name = Name ,
209- resource = Resource }}).
206+ init (Conf ) ->
207+ case Conf of
208+ #{name := Name , queue_resource := Resource } ->
209+ update_config (Conf , #? STATE {cfg = # cfg {name = Name ,
210+ resource = Resource }});
211+ _ ->
212+ erlang :error ({bad_init_arg , Conf })
213+ end .
210214
211215update_config (Conf , State ) ->
212216 DLH = maps :get (dead_letter_handler , Conf , undefined ),
@@ -696,9 +700,12 @@ apply_(Meta, {nodeup, Node}, #?STATE{consumers = Cons0,
696700apply_ (_ , {nodedown , _Node }, State ) ->
697701 {State , ok };
698702apply_ (Meta , # purge_nodes {nodes = Nodes }, State0 ) ->
699- {State , Effects } = lists :foldl (fun (Node , {S , E }) ->
703+ {State1 , Effects } = lists :foldl (fun (Node , {S , E }) ->
700704 purge_node (Meta , Node , S , E )
701705 end , {State0 , []}, Nodes ),
706+ State = State1 #? STATE {
707+ ingress_bytes_by_node =
708+ maps :without (Nodes , State1 #? STATE .ingress_bytes_by_node )},
702709 {State , ok , Effects };
703710apply_ (Meta ,
704711 # update_config {config = #{} = Conf },
@@ -967,8 +974,7 @@ credit_reply_resend_effect(#?STATE{waiting_consumers = Waiting,
967974 end , [], maps :merge (Consumers , maps :from_list (Waiting ))).
968975
969976convert_v8_to_v9 (#{} = _Meta , StateV8 ) ->
970- State = StateV8 ,
971- State .
977+ erlang :append_element (StateV8 , #{}).
972978
973979purge_node (Meta , Node , State , Effects ) ->
974980 lists :foldl (fun (Pid , {S0 , E0 }) ->
@@ -1171,7 +1177,8 @@ overview(#?STATE{consumers = Cons,
11711177 reclaimable_bytes_count => ReclaimableBytes ,
11721178 smallest_raft_index => smallest_raft_index (State ),
11731179 num_active_priorities => NumActivePriorities ,
1174- messages_by_priority => Detail
1180+ messages_by_priority => Detail ,
1181+ ingress_bytes_by_node => State #? STATE .ingress_bytes_by_node
11751182 },
11761183 DlxOverview = dlx_overview (DlxState ),
11771184 maps :merge (maps :merge (Overview , DlxOverview ), SacOverview ).
@@ -1205,7 +1212,13 @@ which_module(7) -> rabbit_fifo_v7;
12051212which_module (8 ) -> rabbit_fifo_v8 ;
12061213which_module (9 ) -> ? MODULE .
12071214
1208- -define (AUX , aux_v4 ).
1215+ -define (AUX , aux_v5 ).
1216+ -define (DEFAULT_INGRESS_DECAY_MS , 60_000 ).
1217+
1218+ -record (ingress_aux ,
1219+ {last_totals = #{} :: #{node () | undefined => non_neg_integer ()},
1220+ estimators = #{} :: #{node () | undefined => ra_li :state ()},
1221+ decay_ms = ? DEFAULT_INGRESS_DECAY_MS :: pos_integer ()}).
12091222
12101223-record (snapshot , {index :: ra :index (),
12111224 timestamp :: milliseconds (),
@@ -1220,7 +1233,8 @@ which_module(9) -> ?MODULE.
12201233 gc = # aux_gc {} :: # aux_gc {},
12211234 tick_pid :: undefined | pid (),
12221235 cache = #{} :: map (),
1223- last_checkpoint :: tuple () | # snapshot {}
1236+ last_checkpoint :: tuple () | # snapshot {},
1237+ ingress = # ingress_aux {} :: # ingress_aux {}
12241238 }).
12251239
12261240init_aux (Name ) when is_atom (Name ) ->
@@ -1234,11 +1248,38 @@ init_aux(Name) when is_atom(Name) ->
12341248 ? SNAP_MIN_RECLAIMABLE_B }),
12351249 Range = max (1 , SnapMinReclaimable - ? SNAP_MIN_RECLAIMABLE_LOW_B ),
12361250 MinReclaimable = ? SNAP_MIN_RECLAIMABLE_LOW_B + rand :uniform (Range ),
1251+ DecayMs = persistent_term :get (rabbit_fifo_ingress_decay_ms ,
1252+ ? DEFAULT_INGRESS_DECAY_MS ),
12371253 #? AUX {name = Name ,
12381254 last_checkpoint = # snapshot {index = 0 ,
12391255 timestamp = erlang :system_time (millisecond ),
12401256 messages_total = 0 ,
1241- min_reclaimable = MinReclaimable }}.
1257+ min_reclaimable = MinReclaimable },
1258+ ingress = # ingress_aux {decay_ms = DecayMs }}.
1259+
1260+ update_ingress (Overview , Nodes , # ingress_aux {last_totals = LastTotals ,
1261+ estimators = Estimators0 ,
1262+ decay_ms = DecayMs } = Ingress ) ->
1263+ NewTotals = maps :get (ingress_bytes_by_node , Overview , #{}),
1264+ Ts = erlang :monotonic_time (millisecond ),
1265+ Estimators1 =
1266+ maps :fold (fun (Node , NewTotal , Est ) ->
1267+ Delta = NewTotal - maps :get (Node , LastTotals , 0 ),
1268+ Li0 = maps :get (Node , Est , ra_li :new (DecayMs )),
1269+ Li1 = ra_li :update (Delta , Ts , Li0 ),
1270+ Est #{Node => Li1 }
1271+ end , Estimators0 , NewTotals ),
1272+ ActiveNodes = sets :from_list (Nodes , [{version , 2 }]),
1273+ Estimators = maps :filter (fun (Node , _ ) ->
1274+ Node =:= undefined orelse
1275+ sets :is_element (Node , ActiveNodes )
1276+ end , Estimators1 ),
1277+ Ingress # ingress_aux {last_totals = NewTotals ,
1278+ estimators = Estimators }.
1279+
1280+ compute_ingress_rates (# ingress_aux {estimators = Estimators }) ->
1281+ Ts = erlang :monotonic_time (millisecond ),
1282+ maps :map (fun (_Node , Li ) -> ra_li :rate (Ts , Li ) end , Estimators ).
12421283
12431284handle_aux (RaftState , Tag , Cmd , AuxV2 , RaAux )
12441285 when element (1 , AuxV2 ) == aux_v2 ->
@@ -1256,6 +1297,19 @@ handle_aux(RaftState, Tag, Cmd, AuxV3, RaAux)
12561297 last_checkpoint = element (8 , AuxV3 )
12571298 },
12581299 handle_aux (RaftState , Tag , Cmd , AuxV4 , RaAux );
1300+ handle_aux (RaftState , Tag , Cmd , AuxV4 , RaAux )
1301+ when element (1 , AuxV4 ) == aux_v4 ->
1302+ DecayMs = persistent_term :get (rabbit_fifo_ingress_decay_ms ,
1303+ ? DEFAULT_INGRESS_DECAY_MS ),
1304+ AuxV5 = #? AUX {name = element (2 , AuxV4 ),
1305+ last_decorators_state = element (3 , AuxV4 ),
1306+ last_consumer_timeout = element (4 , AuxV4 ),
1307+ gc = element (5 , AuxV4 ),
1308+ tick_pid = element (6 , AuxV4 ),
1309+ cache = element (7 , AuxV4 ),
1310+ last_checkpoint = element (8 , AuxV4 ),
1311+ ingress = # ingress_aux {decay_ms = DecayMs }},
1312+ handle_aux (RaftState , Tag , Cmd , AuxV5 , RaAux );
12591313handle_aux (leader , cast , eval ,
12601314 #? AUX {last_decorators_state = LastDec ,
12611315 last_consumer_timeout = LastConTimeout0 ,
@@ -1348,22 +1402,25 @@ handle_aux(_RaftState, cast, {#return{msg_ids = MsgIds,
13481402 % % for returns with a delivery limit set we can just return as before
13491403 {no_reply , Aux0 , RaAux0 , [{append , Ret , {notify , Corr , Pid }}]}
13501404 end ;
1351- handle_aux (leader , _ , {handle_tick , [QName , Overview0 , Nodes ]},
1352- #? AUX {tick_pid = Pid } = Aux , RaAux ) ->
1353- Overview = Overview0 #{members_info => ra_aux :members_info (RaAux )},
1354- NewPid =
1355- case process_is_alive (Pid ) of
1356- false ->
1357- % % No active TICK pid
1358- % % this function spawns and returns the tick process pid
1359- rabbit_quorum_queue :handle_tick (QName , Overview , Nodes );
1360- true ->
1361- % % Active TICK pid, do nothing
1362- Pid
1363- end ,
13641405
1365- % % TODO: check consumer timeouts
1366- {no_reply , Aux #? AUX {tick_pid = NewPid }, RaAux , []};
1406+ handle_aux (RaftState , _ , {handle_tick , [QName , Overview0 , Nodes ]},
1407+ #? AUX {tick_pid = Pid , ingress = Ingress0 } = Aux , RaAux ) ->
1408+ Overview = Overview0 #{members_info => ra_aux :members_info (RaAux )},
1409+ Ingress = update_ingress (Overview0 , Nodes , Ingress0 ),
1410+ Aux1 = Aux #? AUX {ingress = Ingress },
1411+ case RaftState of
1412+ leader ->
1413+ NewPid =
1414+ case process_is_alive (Pid ) of
1415+ false ->
1416+ rabbit_quorum_queue :handle_tick (QName , Overview , Nodes );
1417+ true ->
1418+ Pid
1419+ end ,
1420+ {no_reply , Aux1 #? AUX {tick_pid = NewPid }, RaAux , []};
1421+ _ ->
1422+ {no_reply , Aux1 , RaAux , []}
1423+ end ;
13671424handle_aux (_ , _ , {get_checked_out , ConsumerKey , MsgIds }, Aux0 , RaAux0 ) ->
13681425 #? STATE {cfg = # cfg {},
13691426 consumers = Consumers } = ra_aux :machine_state (RaAux0 ),
@@ -1460,6 +1517,10 @@ handle_aux(leader, _, {dlx, setup}, Aux, RaAux) ->
14601517handle_aux (_ , _ , {dlx , teardown , Pid }, Aux , RaAux ) ->
14611518 terminate_dlx_worker (Pid ),
14621519 {no_reply , Aux , RaAux };
1520+ handle_aux (_ , {call , _From }, get_ingress_rates ,
1521+ #? AUX {ingress = Ingress } = Aux , RaAux ) ->
1522+ Rates = compute_ingress_rates (Ingress ),
1523+ {reply , {ok , Rates }, Aux , RaAux };
14631524handle_aux (_ , _ , Unhandled , Aux , RaAux ) ->
14641525 #? STATE {cfg = # cfg {resource = QR }} = ra_aux :machine_state (RaAux ),
14651526 ? LOG_DEBUG (" ~ts : rabbit_fifo: unhandled aux command ~P " ,
@@ -1907,12 +1968,24 @@ maybe_return_all(#{system_time := Ts} = Meta, ConsumerKey,
19071968 Effects }
19081969 end .
19091970
1971+ node_of (undefined ) -> undefined ;
1972+ node_of (Pid ) when is_pid (Pid ) -> node (Pid ).
1973+
1974+ bump_ingress (Node , Size , Map ) ->
1975+ maps :update_with (Node , fun (V ) -> V + Size end , Size , Map ).
1976+
19101977apply_enqueue (#{index := RaftIdx ,
19111978 system_time := Ts } = Meta , From ,
19121979 Seq , RawMsg , Size , State0 ) ->
19131980 case maybe_enqueue (RaftIdx , Ts , From , Seq , RawMsg , Size , [], State0 ) of
19141981 {ok , State1 , Effects1 } ->
1915- checkout (Meta , State0 , State1 , Effects1 );
1982+ {MetaSize , BodySize } = Size ,
1983+ TotalSize = MetaSize + BodySize ,
1984+ IngressByNode = State1 #? STATE .ingress_bytes_by_node ,
1985+ State2 = State1 #? STATE {
1986+ ingress_bytes_by_node =
1987+ bump_ingress (node_of (From ), TotalSize , IngressByNode )},
1988+ checkout (Meta , State0 , State2 , Effects1 );
19161989 {out_of_sequence , State , Effects } ->
19171990 {State , not_enqueued , Effects };
19181991 {duplicate , State , Effects } ->
0 commit comments