Skip to content

Commit 5213c32

Browse files
Loïc Hoguinmichaelklishin
Loïc Hoguin
authored andcommitted
Add queue_index_segment_entry_count configuration
The default value of ?SEGMENT_ENTRY_COUNT is 16384. Due to the index file format the entire segment file has to be loaded into memory whenever messages from this segment must be accessed. This can result in hundreds of kilobytes that are read, processed and converted to Erlang terms. This creates a lot of garbage in memory, and this garbage unfortunately ends up in the old heap and so cannot be reclaimed before a full GC. Even when forcing a full GC every run (fullsweep_after=0) the process ends up allocating a lot of memory to read segment files and this can create issues in some scenarios. While this is not a problem when there are only a small number of queues, this becomes a showstopper when the number of queues is more important (hundreds or thousands of queues). This only applies to classic/lazy queues. This commit allows configuring the segment file entry count so that the size of the file can be greatly reduced, as well as the memory footprint when reading from the file becomes necessary. Experiments using a segment entry count of 1024 show no noticeable downside other than the natural increase in the number of segment files. The segment entry count can only be set on nodes that have no messages in their queue index. This is because the index uses this value to calculate in which segment file specific messages are sitting in.
1 parent b3b4b89 commit 5213c32

File tree

3 files changed

+27
-16
lines changed

3 files changed

+27
-16
lines changed

deps/rabbit/Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,12 @@ define PROJECT_ENV
119119
%% interval at which connection/channel tracking executes post operations
120120
{tracking_execution_timeout, 15000},
121121
{stream_messages_soft_limit, 256},
122-
{track_auth_attempt_source, false}
122+
{track_auth_attempt_source, false},
123+
%% Number of entries per index segment.
124+
%% This value can only be changed safely
125+
%% on an empty node. Default calculated
126+
%% as trunc(math:pow(2,?REL_SEQ_BITS))).
127+
{queue_index_segment_entry_count, 16384}
123128
]
124129
endef
125130

deps/rabbit/src/rabbit_queue_index.erl

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
-module(rabbit_queue_index).
99

10+
-compile({inline, [segment_entry_count/0]}).
11+
1012
-export([erase/1, init/3, reset_state/1, recover/6,
1113
terminate/3, delete_and_terminate/1,
1214
pre_publish/7, flush_pre_publish_cache/2,
@@ -43,13 +45,13 @@
4345
%% then delivered, then ack'd.
4446
%%
4547
%% In order to be able to clean up ack'd messages, we write to segment
46-
%% files. These files have a fixed number of entries: ?SEGMENT_ENTRY_COUNT
48+
%% files. These files have a fixed number of entries: segment_entry_count()
4749
%% publishes, delivers and acknowledgements. They are numbered, and so
4850
%% it is known that the 0th segment contains messages 0 ->
49-
%% ?SEGMENT_ENTRY_COUNT - 1, the 1st segment contains messages
50-
%% ?SEGMENT_ENTRY_COUNT -> 2*?SEGMENT_ENTRY_COUNT - 1 and so on. As
51+
%% segment_entry_count() - 1, the 1st segment contains messages
52+
%% segment_entry_count() -> 2*segment_entry_count() - 1 and so on. As
5153
%% such, in the segment files, we only refer to message sequence ids
52-
%% by the LSBs as SeqId rem ?SEGMENT_ENTRY_COUNT. This gives them a
54+
%% by the LSBs as SeqId rem segment_entry_count(). This gives them a
5355
%% fixed size.
5456
%%
5557
%% However, transient messages which are not sent to disk at any point
@@ -127,8 +129,6 @@
127129
%% binary generation/matching with constant vs variable lengths.
128130

129131
-define(REL_SEQ_BITS, 14).
130-
%% calculated as trunc(math:pow(2,?REL_SEQ_BITS))).
131-
-define(SEGMENT_ENTRY_COUNT, 16384).
132132

133133
%% seq only is binary 01 followed by 14 bits of rel seq id
134134
%% (range: 0 - 16383)
@@ -352,11 +352,11 @@ pre_publish(MsgOrId, SeqId, MsgProps, IsPersistent, IsDelivered, JournalSizeHint
352352
%% pre_publish_cache is the entry with most elements when compared to
353353
%% delivered_cache so we only check the former in the guard.
354354
maybe_flush_pre_publish_cache(JournalSizeHint,
355-
#qistate{pre_publish_cache = PPC} = State)
356-
when length(PPC) >= ?SEGMENT_ENTRY_COUNT ->
357-
flush_pre_publish_cache(JournalSizeHint, State);
358-
maybe_flush_pre_publish_cache(_JournalSizeHint, State) ->
359-
State.
355+
#qistate{pre_publish_cache = PPC} = State) ->
356+
case length(PPC) >= segment_entry_count() of
357+
true -> flush_pre_publish_cache(JournalSizeHint, State);
358+
false -> State
359+
end.
360360

361361
flush_pre_publish_cache(JournalSizeHint, State) ->
362362
State1 = flush_pre_publish_cache(State),
@@ -991,10 +991,11 @@ notify_sync(State = #qistate{unconfirmed = UC,
991991
%%----------------------------------------------------------------------------
992992

993993
seq_id_to_seg_and_rel_seq_id(SeqId) ->
994-
{ SeqId div ?SEGMENT_ENTRY_COUNT, SeqId rem ?SEGMENT_ENTRY_COUNT }.
994+
SegmentEntryCount = segment_entry_count(),
995+
{ SeqId div SegmentEntryCount, SeqId rem SegmentEntryCount }.
995996

996997
reconstruct_seq_id(Seg, RelSeq) ->
997-
(Seg * ?SEGMENT_ENTRY_COUNT) + RelSeq.
998+
(Seg * segment_entry_count()) + RelSeq.
998999

9991000
all_segment_nums(#qistate { dir = Dir, segments = Segments }) ->
10001001
lists:sort(
@@ -1163,7 +1164,12 @@ array_new() ->
11631164
array_new(undefined).
11641165

11651166
array_new(Default) ->
1166-
array:new([{default, Default}, fixed, {size, ?SEGMENT_ENTRY_COUNT}]).
1167+
array:new([{default, Default}, fixed, {size, segment_entry_count()}]).
1168+
1169+
segment_entry_count() ->
1170+
{ok, SegmentEntryCount} =
1171+
application:get_env(rabbit, queue_index_segment_entry_count),
1172+
SegmentEntryCount.
11671173

11681174
bool_to_int(true ) -> 1;
11691175
bool_to_int(false) -> 0.

deps/rabbit/src/rabbit_variable_queue.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,7 @@ a(State = #vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4,
11101110
%% disk). See push_alphas_to_betas/2.
11111111
true = E2 or not ED,
11121112
%% if delta has messages then q3 cannot be empty. This is enforced
1113-
%% by paging, where min([?SEGMENT_ENTRY_COUNT, len(q3)]) messages
1113+
%% by paging, where min([segment_entry_count(), len(q3)]) messages
11141114
%% are always kept on RAM.
11151115
true = ED or not E3,
11161116
%% if the queue length is 0, then q3 and q4 must be empty.

0 commit comments

Comments
 (0)