Skip to content

Commit 7ee76ef

Browse files
committed
Optimise messages_ready function by keeping counts of prefix messages
rather than taking length/1. There could be a lot of prefix messages during during recovery which could make recovery unbearably slow.
1 parent 5ce141c commit 7ee76ef

File tree

3 files changed

+42
-25
lines changed

3 files changed

+42
-25
lines changed

src/rabbit_fifo.erl

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ apply(#{index := RaftIdx}, #purge{},
327327
messages = #{},
328328
returns = lqueue:new(),
329329
msg_bytes_enqueue = 0,
330-
prefix_msgs = {[], []},
330+
prefix_msgs = {0, [], 0, []},
331331
low_msg_num = undefined,
332332
msg_bytes_in_memory = 0,
333333
msgs_ready_in_memory = 0},
@@ -555,7 +555,7 @@ state_enter(leader, #?MODULE{consumers = Cons,
555555
cfg = #cfg{name = Name,
556556
resource = Resource,
557557
become_leader_handler = BLH},
558-
prefix_msgs = {[], []}
558+
prefix_msgs = {0, [], 0, []}
559559
}) ->
560560
% return effects to monitor all current consumers and enqueuers
561561
Pids = lists:usort(maps:keys(Enqs)
@@ -770,16 +770,16 @@ usage(Name) when is_atom(Name) ->
770770
%%% Internal
771771

772772
messages_ready(#?MODULE{messages = M,
773-
prefix_msgs = {PreR, PreM},
773+
prefix_msgs = {RCnt, _R, PCnt, _P},
774774
returns = R}) ->
775775

776776
%% prefix messages will rarely have anything in them during normal
777777
%% operations so length/1 is fine here
778-
maps:size(M) + lqueue:len(R) + length(PreR) + length(PreM).
778+
maps:size(M) + lqueue:len(R) + RCnt + PCnt.
779779

780780
messages_total(#?MODULE{ra_indexes = I,
781-
prefix_msgs = {PreR, PreM}}) ->
782-
rabbit_fifo_index:size(I) + length(PreR) + length(PreM).
781+
prefix_msgs = {RCnt, _R, PCnt, _P}}) ->
782+
rabbit_fifo_index:size(I) + RCnt + PCnt.
783783

784784
update_use({inactive, _, _, _} = CUInfo, inactive) ->
785785
CUInfo;
@@ -1016,13 +1016,15 @@ maybe_store_dehydrated_state(RaftIdx,
10161016
= Cfg,
10171017
ra_indexes = Indexes,
10181018
enqueue_count = 0,
1019-
release_cursors = Cursors0} = State) ->
1019+
release_cursors = Cursors0} = State0) ->
10201020
case rabbit_fifo_index:exists(RaftIdx, Indexes) of
10211021
false ->
10221022
%% the incoming enqueue must already have been dropped
1023-
State;
1023+
State0;
10241024
true ->
1025-
Dehydrated = dehydrate_state(State),
1025+
State = convert_prefix_msgs(State0),
1026+
{Time, Dehydrated} = timer:tc(fun () -> dehydrate_state(State) end),
1027+
rabbit_log:info("dehydrating state took ~bms", [Time div 1000]),
10261028
Cursor = {release_cursor, RaftIdx, Dehydrated},
10271029
Cursors = lqueue:in(Cursor, Cursors0),
10281030
Interval = lqueue:len(Cursors) * Base,
@@ -1336,7 +1338,8 @@ evaluate_limit(Result,
13361338
max_bytes = undefined}} = State,
13371339
Effects) ->
13381340
{State, Result, Effects};
1339-
evaluate_limit(Result, State0, Effects0) ->
1341+
evaluate_limit(Result, State00, Effects0) ->
1342+
State0 = convert_prefix_msgs(State00),
13401343
case is_over_limit(State0) of
13411344
true ->
13421345
{State, Effects} = drop_head(State0, Effects0),
@@ -1380,17 +1383,21 @@ append_log_effects(Effects0, AccMap) ->
13801383
%%
13811384
%% When we return it is always done to the current return queue
13821385
%% for both prefix messages and current messages
1383-
take_next_msg(#?MODULE{prefix_msgs = {[{'$empty_msg', _} = Msg | Rem], P}} = State) ->
1386+
take_next_msg(#?MODULE{prefix_msgs = {R, P}} = State) ->
1387+
%% conversion
1388+
take_next_msg(State#?MODULE{prefix_msgs = {length(R), R, length(P), P}});
1389+
take_next_msg(#?MODULE{prefix_msgs = {NumR, [{'$empty_msg', _} = Msg | Rem],
1390+
NumP, P}} = State) ->
13841391
%% there are prefix returns, these should be served first
1385-
{Msg, State#?MODULE{prefix_msgs = {Rem, P}}};
1386-
take_next_msg(#?MODULE{prefix_msgs = {[Header | Rem], P}} = State) ->
1392+
{Msg, State#?MODULE{prefix_msgs = {NumR-1, Rem, NumP, P}}};
1393+
take_next_msg(#?MODULE{prefix_msgs = {NumR, [Header | Rem], NumP, P}} = State) ->
13871394
%% there are prefix returns, these should be served first
13881395
{{'$prefix_msg', Header},
1389-
State#?MODULE{prefix_msgs = {Rem, P}}};
1396+
State#?MODULE{prefix_msgs = {NumR-1, Rem, NumP, P}}};
13901397
take_next_msg(#?MODULE{returns = Returns,
13911398
low_msg_num = Low0,
13921399
messages = Messages0,
1393-
prefix_msgs = {R, P}} = State) ->
1400+
prefix_msgs = {NumR, R, NumP, P}} = State) ->
13941401
%% use peek rather than out there as the most likely case is an empty
13951402
%% queue
13961403
case lqueue:peek(Returns) of
@@ -1420,10 +1427,10 @@ take_next_msg(#?MODULE{returns = Returns,
14201427
{Header, 'empty'} ->
14211428
%% There are prefix msgs
14221429
{{'$empty_msg', Header},
1423-
State#?MODULE{prefix_msgs = {R, Rem}}};
1430+
State#?MODULE{prefix_msgs = {NumR, R, NumP-1, Rem}}};
14241431
Header ->
14251432
{{'$prefix_msg', Header},
1426-
State#?MODULE{prefix_msgs = {R, Rem}}}
1433+
State#?MODULE{prefix_msgs = {NumR, R, NumP-1, Rem}}}
14271434
end
14281435
end.
14291436

@@ -1600,17 +1607,23 @@ maybe_queue_consumer(ConsumerId, #consumer{credit = Credit},
16001607
ServiceQueue0
16011608
end.
16021609

1610+
convert_prefix_msgs(#?MODULE{prefix_msgs = {R, P}} = State) ->
1611+
State#?MODULE{prefix_msgs = {length(R), R, length(P), P}};
1612+
convert_prefix_msgs(State) ->
1613+
State.
1614+
16031615
%% creates a dehydrated version of the current state to be cached and
16041616
%% potentially used to for a snaphot at a later point
16051617
dehydrate_state(#?MODULE{messages = Messages,
16061618
consumers = Consumers,
16071619
returns = Returns,
16081620
low_msg_num = Low,
16091621
next_msg_num = Next,
1610-
prefix_msgs = {PrefRet0, PrefMsg0},
1622+
prefix_msgs = {PRCnt, PrefRet0, PPCnt, PrefMsg0},
16111623
waiting_consumers = Waiting0} = State) ->
1624+
RCnt = lqueue:len(Returns),
16121625
%% TODO: optimise this function as far as possible
1613-
PrefRet = lists:foldl(fun ({'$prefix_msg', Header}, Acc) ->
1626+
PrefRet1 = lists:foldr(fun ({'$prefix_msg', Header}, Acc) ->
16141627
[Header | Acc];
16151628
({'$empty_msg', _} = Msg, Acc) ->
16161629
[Msg | Acc];
@@ -1619,8 +1632,9 @@ dehydrate_state(#?MODULE{messages = Messages,
16191632
({_, {_, {Header, _}}}, Acc) ->
16201633
[Header | Acc]
16211634
end,
1622-
lists:reverse(PrefRet0),
1635+
[],
16231636
lqueue:to_list(Returns)),
1637+
PrefRet = PrefRet0 ++ PrefRet1,
16241638
PrefMsgsSuff = dehydrate_messages(Low, Next - 1, Messages, []),
16251639
%% prefix messages are not populated in normal operation only after
16261640
%% recovering from a snapshot
@@ -1634,8 +1648,8 @@ dehydrate_state(#?MODULE{messages = Messages,
16341648
dehydrate_consumer(C)
16351649
end, Consumers),
16361650
returns = lqueue:new(),
1637-
prefix_msgs = {lists:reverse(PrefRet),
1638-
PrefMsgs},
1651+
prefix_msgs = {PRCnt + RCnt, PrefRet,
1652+
PPCnt + maps:size(Messages), PrefMsgs},
16391653
waiting_consumers = Waiting}.
16401654

16411655
dehydrate_messages(Low, Next, _Msgs, Acc)

src/rabbit_fifo.hrl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@
119119
max_in_memory_bytes :: option(non_neg_integer())
120120
}).
121121

122+
-type prefix_msgs() :: {list(), list()} |
123+
{non_neg_integer(), list(),
124+
non_neg_integer(), list()}.
125+
122126
-record(rabbit_fifo,
123127
{cfg :: #cfg{},
124128
% unassigned messages
@@ -161,8 +165,7 @@
161165
%% overflow calculations).
162166
%% This is done so that consumers are still served in a deterministic
163167
%% order on recovery.
164-
prefix_msgs = {[], []} :: {Return :: [msg_header() | {'$empty_msg', msg_header()}],
165-
PrefixMsgs :: [msg_header() | {msg_header(), 'empty'}]},
168+
prefix_msgs = {0, [], 0, []} :: prefix_msgs(),
166169
msg_bytes_enqueue = 0 :: non_neg_integer(),
167170
msg_bytes_checkout = 0 :: non_neg_integer(),
168171
%% waiting consumers, one is picked active consumer is cancelled or dies

src/rabbit_quorum_queue.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ all_replica_states() ->
249249
list_with_minimum_quorum() ->
250250
filter_quorum_critical(rabbit_amqqueue:list_local_quorum_queues()).
251251

252-
-spec list_with_minimum_quorum_for_cli() -> [amqqueue:amqqueue()].
252+
-spec list_with_minimum_quorum_for_cli() -> [#{binary() => term()}].
253253
list_with_minimum_quorum_for_cli() ->
254254
QQs = list_with_minimum_quorum(),
255255
[begin

0 commit comments

Comments
 (0)