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 :display ({bad_init_arg , Conf }),
213+ erlang :error ({bad_init_arg , Conf })
214+ end .
210215
211216update_config (Conf , State ) ->
212217 DLH = maps :get (dead_letter_handler , Conf , undefined ),
@@ -696,9 +701,12 @@ apply_(Meta, {nodeup, Node}, #?STATE{consumers = Cons0,
696701apply_ (_ , {nodedown , _Node }, State ) ->
697702 {State , ok };
698703apply_ (Meta , # purge_nodes {nodes = Nodes }, State0 ) ->
699- {State , Effects } = lists :foldl (fun (Node , {S , E }) ->
704+ {State1 , Effects } = lists :foldl (fun (Node , {S , E }) ->
700705 purge_node (Meta , Node , S , E )
701706 end , {State0 , []}, Nodes ),
707+ State = State1 #? STATE {
708+ ingress_bytes_by_node =
709+ maps :without (Nodes , State1 #? STATE .ingress_bytes_by_node )},
702710 {State , ok , Effects };
703711apply_ (Meta ,
704712 # update_config {config = #{} = Conf },
@@ -967,8 +975,7 @@ credit_reply_resend_effect(#?STATE{waiting_consumers = Waiting,
967975 end , [], maps :merge (Consumers , maps :from_list (Waiting ))).
968976
969977convert_v8_to_v9 (#{} = _Meta , StateV8 ) ->
970- State = StateV8 ,
971- State .
978+ erlang :append_element (StateV8 , #{}).
972979
973980purge_node (Meta , Node , State , Effects ) ->
974981 lists :foldl (fun (Pid , {S0 , E0 }) ->
@@ -1171,7 +1178,8 @@ overview(#?STATE{consumers = Cons,
11711178 reclaimable_bytes_count => ReclaimableBytes ,
11721179 smallest_raft_index => smallest_raft_index (State ),
11731180 num_active_priorities => NumActivePriorities ,
1174- messages_by_priority => Detail
1181+ messages_by_priority => Detail ,
1182+ ingress_bytes_by_node => State #? STATE .ingress_bytes_by_node
11751183 },
11761184 DlxOverview = dlx_overview (DlxState ),
11771185 maps :merge (maps :merge (Overview , DlxOverview ), SacOverview ).
@@ -1205,7 +1213,13 @@ which_module(7) -> rabbit_fifo_v7;
12051213which_module (8 ) -> rabbit_fifo_v8 ;
12061214which_module (9 ) -> ? MODULE .
12071215
1208- -define (AUX , aux_v4 ).
1216+ -define (AUX , aux_v5 ).
1217+ -define (DEFAULT_INGRESS_DECAY_MS , 60_000 ).
1218+
1219+ -record (ingress_aux ,
1220+ {last_totals = #{} :: #{node () | undefined => non_neg_integer ()},
1221+ estimators = #{} :: #{node () | undefined => ra_li :state ()},
1222+ decay_ms = ? DEFAULT_INGRESS_DECAY_MS :: pos_integer ()}).
12091223
12101224-record (snapshot , {index :: ra :index (),
12111225 timestamp :: milliseconds (),
@@ -1220,7 +1234,8 @@ which_module(9) -> ?MODULE.
12201234 gc = # aux_gc {} :: # aux_gc {},
12211235 tick_pid :: undefined | pid (),
12221236 cache = #{} :: map (),
1223- last_checkpoint :: tuple () | # snapshot {}
1237+ last_checkpoint :: tuple () | # snapshot {},
1238+ ingress = # ingress_aux {} :: # ingress_aux {}
12241239 }).
12251240
12261241init_aux (Name ) when is_atom (Name ) ->
@@ -1234,11 +1249,38 @@ init_aux(Name) when is_atom(Name) ->
12341249 ? SNAP_MIN_RECLAIMABLE_B }),
12351250 Range = max (1 , SnapMinReclaimable - ? SNAP_MIN_RECLAIMABLE_LOW_B ),
12361251 MinReclaimable = ? SNAP_MIN_RECLAIMABLE_LOW_B + rand :uniform (Range ),
1252+ DecayMs = persistent_term :get (rabbit_fifo_ingress_decay_ms ,
1253+ ? DEFAULT_INGRESS_DECAY_MS ),
12371254 #? AUX {name = Name ,
12381255 last_checkpoint = # snapshot {index = 0 ,
12391256 timestamp = erlang :system_time (millisecond ),
12401257 messages_total = 0 ,
1241- min_reclaimable = MinReclaimable }}.
1258+ min_reclaimable = MinReclaimable },
1259+ ingress = # ingress_aux {decay_ms = DecayMs }}.
1260+
1261+ update_ingress (Overview , Nodes , # ingress_aux {last_totals = LastTotals ,
1262+ estimators = Estimators0 ,
1263+ decay_ms = DecayMs } = Ingress ) ->
1264+ NewTotals = maps :get (ingress_bytes_by_node , Overview , #{}),
1265+ Ts = erlang :monotonic_time (millisecond ),
1266+ Estimators1 =
1267+ maps :fold (fun (Node , NewTotal , Est ) ->
1268+ Delta = NewTotal - maps :get (Node , LastTotals , 0 ),
1269+ Li0 = maps :get (Node , Est , ra_li :new (DecayMs )),
1270+ Li1 = ra_li :update (Delta , Ts , Li0 ),
1271+ Est #{Node => Li1 }
1272+ end , Estimators0 , NewTotals ),
1273+ ActiveNodes = sets :from_list (Nodes , [{version , 2 }]),
1274+ Estimators = maps :filter (fun (Node , _ ) ->
1275+ Node =:= undefined orelse
1276+ sets :is_element (Node , ActiveNodes )
1277+ end , Estimators1 ),
1278+ Ingress # ingress_aux {last_totals = NewTotals ,
1279+ estimators = Estimators }.
1280+
1281+ compute_ingress_rates (# ingress_aux {estimators = Estimators }) ->
1282+ Ts = erlang :monotonic_time (millisecond ),
1283+ maps :map (fun (_Node , Li ) -> ra_li :rate (Ts , Li ) end , Estimators ).
12421284
12431285handle_aux (RaftState , Tag , Cmd , AuxV2 , RaAux )
12441286 when element (1 , AuxV2 ) == aux_v2 ->
@@ -1256,6 +1298,19 @@ handle_aux(RaftState, Tag, Cmd, AuxV3, RaAux)
12561298 last_checkpoint = element (8 , AuxV3 )
12571299 },
12581300 handle_aux (RaftState , Tag , Cmd , AuxV4 , RaAux );
1301+ handle_aux (RaftState , Tag , Cmd , AuxV4 , RaAux )
1302+ when element (1 , AuxV4 ) == aux_v4 ->
1303+ DecayMs = persistent_term :get (rabbit_fifo_ingress_decay_ms ,
1304+ ? DEFAULT_INGRESS_DECAY_MS ),
1305+ AuxV5 = #? AUX {name = element (2 , AuxV4 ),
1306+ last_decorators_state = element (3 , AuxV4 ),
1307+ last_consumer_timeout = element (4 , AuxV4 ),
1308+ gc = element (5 , AuxV4 ),
1309+ tick_pid = element (6 , AuxV4 ),
1310+ cache = element (7 , AuxV4 ),
1311+ last_checkpoint = element (8 , AuxV4 ),
1312+ ingress = # ingress_aux {decay_ms = DecayMs }},
1313+ handle_aux (RaftState , Tag , Cmd , AuxV5 , RaAux );
12591314handle_aux (leader , cast , eval ,
12601315 #? AUX {last_decorators_state = LastDec ,
12611316 last_consumer_timeout = LastConTimeout0 ,
@@ -1348,22 +1403,25 @@ handle_aux(_RaftState, cast, {#return{msg_ids = MsgIds,
13481403 % % for returns with a delivery limit set we can just return as before
13491404 {no_reply , Aux0 , RaAux0 , [{append , Ret , {notify , Corr , Pid }}]}
13501405 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 ,
13641406
1365- % % TODO: check consumer timeouts
1366- {no_reply , Aux #? AUX {tick_pid = NewPid }, RaAux , []};
1407+ handle_aux (RaftState , _ , {handle_tick , [QName , Overview0 , Nodes ]},
1408+ #? AUX {tick_pid = Pid , ingress = Ingress0 } = Aux , RaAux ) ->
1409+ Overview = Overview0 #{members_info => ra_aux :members_info (RaAux )},
1410+ Ingress = update_ingress (Overview0 , Nodes , Ingress0 ),
1411+ Aux1 = Aux #? AUX {ingress = Ingress },
1412+ case RaftState of
1413+ leader ->
1414+ NewPid =
1415+ case process_is_alive (Pid ) of
1416+ false ->
1417+ rabbit_quorum_queue :handle_tick (QName , Overview , Nodes );
1418+ true ->
1419+ Pid
1420+ end ,
1421+ {no_reply , Aux1 #? AUX {tick_pid = NewPid }, RaAux , []};
1422+ _ ->
1423+ {no_reply , Aux1 , RaAux , []}
1424+ end ;
13671425handle_aux (_ , _ , {get_checked_out , ConsumerKey , MsgIds }, Aux0 , RaAux0 ) ->
13681426 #? STATE {cfg = # cfg {},
13691427 consumers = Consumers } = ra_aux :machine_state (RaAux0 ),
@@ -1460,6 +1518,10 @@ handle_aux(leader, _, {dlx, setup}, Aux, RaAux) ->
14601518handle_aux (_ , _ , {dlx , teardown , Pid }, Aux , RaAux ) ->
14611519 terminate_dlx_worker (Pid ),
14621520 {no_reply , Aux , RaAux };
1521+ handle_aux (_ , {call , _From }, get_ingress_rates ,
1522+ #? AUX {ingress = Ingress } = Aux , RaAux ) ->
1523+ Rates = compute_ingress_rates (Ingress ),
1524+ {reply , {ok , Rates }, Aux , RaAux };
14631525handle_aux (_ , _ , Unhandled , Aux , RaAux ) ->
14641526 #? STATE {cfg = # cfg {resource = QR }} = ra_aux :machine_state (RaAux ),
14651527 ? LOG_DEBUG (" ~ts : rabbit_fifo: unhandled aux command ~P " ,
@@ -1907,12 +1969,24 @@ maybe_return_all(#{system_time := Ts} = Meta, ConsumerKey,
19071969 Effects }
19081970 end .
19091971
1972+ node_of (undefined ) -> undefined ;
1973+ node_of (Pid ) when is_pid (Pid ) -> node (Pid ).
1974+
1975+ bump_ingress (Node , Size , Map ) ->
1976+ maps :update_with (Node , fun (V ) -> V + Size end , Size , Map ).
1977+
19101978apply_enqueue (#{index := RaftIdx ,
19111979 system_time := Ts } = Meta , From ,
19121980 Seq , RawMsg , Size , State0 ) ->
19131981 case maybe_enqueue (RaftIdx , Ts , From , Seq , RawMsg , Size , [], State0 ) of
19141982 {ok , State1 , Effects1 } ->
1915- checkout (Meta , State0 , State1 , Effects1 );
1983+ {MetaSize , BodySize } = Size ,
1984+ TotalSize = MetaSize + BodySize ,
1985+ IngressByNode = State1 #? STATE .ingress_bytes_by_node ,
1986+ State2 = State1 #? STATE {
1987+ ingress_bytes_by_node =
1988+ bump_ingress (node_of (From ), TotalSize , IngressByNode )},
1989+ checkout (Meta , State0 , State2 , Effects1 );
19161990 {out_of_sequence , State , Effects } ->
19171991 {State , not_enqueued , Effects };
19181992 {duplicate , State , Effects } ->
0 commit comments