Skip to content

Commit 13e1bf8

Browse files
committed
Take live data sizes into account when grouping for compaction
1 parent 4661853 commit 13e1bf8

File tree

7 files changed

+190
-54
lines changed

7 files changed

+190
-54
lines changed

src/ra_log.erl

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ init(#{uid := UId,
188188
segment_writer := SegWriter} = Names}
189189
} = Conf) ->
190190
Dir = server_data_dir(DataDir, UId),
191-
MaxOpen = maps:get(max_open_segments, Conf, 5),
191+
MaxOpen = maps:get(max_open_segments, Conf, 1),
192192
SnapModule = maps:get(snapshot_module, Conf, ?DEFAULT_SNAPSHOT_MODULE),
193193
%% this has to be patched by ra_server
194194
LogId = maps:get(log_id, Conf, UId),
@@ -228,8 +228,12 @@ init(#{uid := UId,
228228
% segments it is currently processed have been finished
229229
MtRange = ra_mt:range(Mt0),
230230
SegRefs = my_segrefs(UId, SegWriter),
231+
SegmentMaxCount = maps:get(segment_max_entries, Conf, ?SEGMENT_MAX_ENTRIES),
232+
SegmentMaxSize = maps:get(segment_max_size_bytes, Conf, ?SEGMENT_MAX_SIZE_B),
233+
CompConf = #{max_size => SegmentMaxSize,
234+
max_count => SegmentMaxCount},
231235
Reader = ra_log_segments:init(UId, Dir, MaxOpen, AccessPattern, SegRefs,
232-
Counter, LogId),
236+
Counter, CompConf, LogId),
233237
SegmentRange = ra_log_segments:range(Reader),
234238
%% TODO: check ra_range:add/2 actually performas the correct logic we expect
235239
Range = ra_range:add(MtRange, SegmentRange),
@@ -1306,13 +1310,14 @@ release_resources(MaxOpenSegments, AccessPattern,
13061310
counter = Counter},
13071311
reader = Reader} = State) ->
13081312
ActiveSegs = ra_log_segments:segment_refs(Reader),
1313+
CompConf = ra_log_segments:compaction_conf(Reader),
13091314
% close all open segments
13101315
% deliberately ignoring return value
13111316
_ = ra_log_segments:close(Reader),
13121317
%% open a new segment with the new max open segment value
13131318
State#?MODULE{reader = ra_log_segments:init(UId, Dir, MaxOpenSegments,
13141319
AccessPattern, ActiveSegs,
1315-
Counter, LogId)}.
1320+
Counter, CompConf, LogId)}.
13161321

13171322

13181323
%%% Local functions

src/ra_log_segment.erl

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
filename/1,
2424
segref/1,
2525
info/1,
26+
info/2,
2627
is_same_as/2,
2728
copy/3]).
2829

@@ -454,17 +455,23 @@ segref(Filename) ->
454455
close(Seg),
455456
SegRef.
456457

457-
-spec info(file:filename_all()) ->
458-
#{size => non_neg_integer(),
459-
max_count => non_neg_integer(),
460-
file_type => regular | symlink,
461-
ctime => integer(),
462-
links => non_neg_integer(),
463-
num_entries => non_neg_integer(),
464-
ref => option(ra_log:segment_ref()),
465-
indexes => ra_seq:state()
466-
}.
467-
info(Filename)
458+
-type infos() :: #{size => non_neg_integer(),
459+
max_count => non_neg_integer(),
460+
file_type => regular | symlink,
461+
ctime => integer(),
462+
links => non_neg_integer(),
463+
num_entries => non_neg_integer(),
464+
ref => option(ra_log:segment_ref()),
465+
indexes => ra_seq:state(),
466+
live_size => non_neg_integer()
467+
}.
468+
469+
-spec info(file:filename_all()) -> infos().
470+
info(Filename) ->
471+
info(Filename, undefined).
472+
473+
-spec info(file:filename_all(), option(ra_seq:state())) -> infos().
474+
info(Filename, Live0)
468475
when not is_tuple(Filename) ->
469476
%% TODO: this can be much optimised by a specialised index parsing
470477
%% function
@@ -475,14 +482,29 @@ info(Filename)
475482
ctime = CTime}} = file:read_link_info(Filename,
476483
[raw, {time, posix}]),
477484

478-
Info = #{size => Seg#state.data_write_offset,
485+
AllIndexesSeq = ra_seq:from_list(maps:keys(Index)),
486+
Live = case Live0 of
487+
undefined ->
488+
AllIndexesSeq;
489+
_ ->
490+
Live0
491+
end,
492+
LiveSize = ra_seq:fold(fun (I, Acc) ->
493+
{_, _, Sz, _} = maps:get(I, Index),
494+
Acc + Sz
495+
end, 0, Live),
496+
Info = #{
497+
size => Seg#state.data_write_offset,
498+
index_size => Seg#state.data_start,
479499
file_type => T,
480500
links => Links,
481501
ctime => CTime,
482502
max_count => max_count(Seg),
483503
num_entries => maps:size(Index),
484504
ref => segref(Seg),
485-
indexes => ra_seq:from_list(maps:keys(Index))
505+
live_size => LiveSize,
506+
%% TODO: this is most likely just here for debugging
507+
indexes => AllIndexesSeq
486508
},
487509
close(Seg),
488510
Info.

src/ra_log_segments.erl

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
-include_lib("kernel/include/file.hrl").
1313
-export([
14-
init/7,
14+
init/8,
1515
close/1,
1616
update_segments/2,
1717
schedule_compaction/4,
@@ -27,7 +27,8 @@
2727
exec_read_plan/6,
2828
fetch_term/2,
2929
info/1,
30-
purge_symlinks/2
30+
purge_symlinks/2,
31+
compaction_conf/1
3132
]).
3233

3334
-include("ra.hrl").
@@ -36,13 +37,16 @@
3637

3738
-define(SYMLINK_KEEPFOR_S, 60).
3839

40+
-type compaction_conf() :: #{max_count => non_neg_integer(),
41+
max_size => non_neg_integer()}.
3942
-type access_pattern() :: sequential | random.
4043
%% holds static or rarely changing fields
4144
-record(cfg, {uid :: ra_uid(),
4245
log_id = "" :: unicode:chardata(),
4346
counter :: undefined | counters:counters_ref(),
4447
directory :: file:filename(),
45-
access_pattern = random :: access_pattern()
48+
access_pattern = random :: access_pattern(),
49+
compaction_conf :: compaction_conf()
4650
}).
4751

4852
-type segment_ref() :: ra_log:segment_ref().
@@ -74,14 +78,16 @@
7478
-spec init(ra_uid(), file:filename_all(), non_neg_integer(),
7579
access_pattern(), [segment_ref()],
7680
undefined | counters:counters_ref(),
81+
map(),
7782
unicode:chardata()) -> state().
78-
init(UId, Dir, MaxOpen, AccessPattern, SegRefs0, Counter, LogId)
83+
init(UId, Dir, MaxOpen, AccessPattern, SegRefs0, Counter, CompConf, LogId)
7984
when is_binary(UId) ->
8085
Cfg = #cfg{uid = UId,
8186
log_id = LogId,
8287
counter = Counter,
8388
directory = Dir,
84-
access_pattern = AccessPattern},
89+
access_pattern = AccessPattern,
90+
compaction_conf = CompConf},
8591
FlruHandler = fun ({_, Seg}) ->
8692
_ = ra_log_segment:close(Seg),
8793
decr_counter(Cfg, ?C_RA_LOG_OPEN_SEGMENTS, 1)
@@ -150,6 +156,7 @@ update_segments(NewSegmentRefs, #?STATE{open_segments = Open0,
150156
[ra_server:effect()].
151157
schedule_compaction(Type, SnapIdx, LiveIndexes,
152158
#?MODULE{cfg = #cfg{log_id = LogId,
159+
compaction_conf = CompConf,
153160
directory = Dir} = Cfg} = State) ->
154161
case compactable_segrefs(SnapIdx, State) of
155162
[] ->
@@ -171,21 +178,25 @@ schedule_compaction(Type, SnapIdx, LiveIndexes,
171178
Self = self(),
172179
Fun = fun () ->
173180
ok = incr_counter(Cfg, ?C_RA_LOG_COMPACTIONS_MAJOR_COUNT, 1),
174-
MajConf = #{dir => Dir},
181+
MajConf = CompConf#{dir => Dir},
175182
Result = major_compaction(MajConf, SegRefs,
176183
LiveIndexes),
177-
%% TODO: this could be done on a timer if more
178-
%% timely symlink cleanup is needed
179-
purge_symlinks(Dir, ?SYMLINK_KEEPFOR_S),
180184
%% need to update the ra_servers list of seg refs
181185
%% _before_ the segments can actually be deleted
182186
Self ! {ra_log_event,
183187
{compaction_result, Result}},
188+
%% TODO: this could be done on a timer if more
189+
%% timely symlink cleanup is needed
190+
purge_symlinks(Dir, ?SYMLINK_KEEPFOR_S),
184191
ok
185192
end,
186193

187194
[{bg_work, Fun,
188195
fun (Err) ->
196+
%% send an empty compaction result to ensure the
197+
%% a future compaction can be performed (TODO:)
198+
Self ! {ra_log_event,
199+
{compaction_result, #compaction_result{}}},
189200
?WARN("~ts: Major compaction failed with ~p",
190201
[LogId, Err]), ok
191202
end}]
@@ -266,6 +277,10 @@ segment_ref_count(#?STATE{segment_refs = SegmentRefs}) ->
266277
range(#?STATE{range = Range}) ->
267278
Range.
268279

280+
-spec compaction_conf(state()) -> map().
281+
compaction_conf(#?STATE{cfg = #cfg{compaction_conf = Conf}}) ->
282+
Conf.
283+
269284
-spec num_open_segments(state()) -> non_neg_integer().
270285
num_open_segments(#?STATE{open_segments = Open}) ->
271286
ra_flru:size(Open).
@@ -572,7 +587,7 @@ list_files(Dir, Ext, Fun) ->
572587
[]
573588
end.
574589

575-
major_compaction(#{dir := Dir}, SegRefs, LiveIndexes) ->
590+
major_compaction(#{dir := Dir} = CompConf, SegRefs, LiveIndexes) ->
576591
{Compactable, Delete} =
577592
lists:foldl(fun({Fn0, Range} = S,
578593
{Comps, Del}) ->
@@ -584,7 +599,7 @@ major_compaction(#{dir := Dir}, SegRefs, LiveIndexes) ->
584599
%% get the info map from each
585600
%% potential segment
586601
Fn = filename:join(Dir, Fn0),
587-
Info = ra_log_segment:info(Fn),
602+
Info = ra_log_segment:info(Fn, Seq),
588603
{[{Info, Seq, S} | Comps], Del}
589604
end
590605
end, {[], []}, SegRefs),
@@ -602,7 +617,8 @@ major_compaction(#{dir := Dir}, SegRefs, LiveIndexes) ->
602617
ok = prim_file:delete(filename:join(Dir, F))
603618
end || F <- UnusedFiles],
604619
%% group compactable
605-
CompactionGroups = compaction_groups(lists:reverse(Compactable), []),
620+
CompactionGroups = compaction_groups(lists:reverse(Compactable), [],
621+
CompConf),
606622
Compacted0 =
607623
[begin
608624
%% create a new segment with .compacting extension
@@ -706,31 +722,37 @@ make_links(Dir, To, From)
706722
with_ext(Fn, Ext) when is_binary(Fn) andalso is_list(Ext) ->
707723
<<(filename:rootname(Fn))/binary, (ra_lib:to_binary(Ext))/binary>>.
708724

709-
compaction_groups([], Groups) ->
725+
compaction_groups([], Groups, _Conf) ->
710726
lists:reverse(Groups);
711-
compaction_groups(Infos, Groups) ->
712-
case take_group(Infos, #{max_count => 128}, []) of
727+
compaction_groups(Infos, Groups, Conf) ->
728+
case take_group(Infos, Conf, []) of
713729
{Group, RemInfos} ->
714-
compaction_groups(RemInfos, [Group | Groups])
730+
compaction_groups(RemInfos, [Group | Groups], Conf)
715731
end.
716732

717733
%% TODO: try to take potential size into account
718734
take_group([], _, Acc) ->
719735
{lists:reverse(Acc), []};
720-
take_group([{#{num_entries := NumEnts}, Live, {_, _}} = E | Rem] = All,
721-
#{max_count := Mc}, Acc) ->
736+
take_group([{#{num_entries := NumEnts,
737+
live_size := LiveSize}, Live, {_, _}} = E | Rem] = All,
738+
#{max_count := Mc,
739+
max_size := MaxSz}, Acc) ->
722740
Num = ra_seq:length(Live),
723-
case Num < NumEnts div 2 of
741+
case Num / NumEnts < 0.5 of
724742
true ->
725-
case Mc - Num < 0 of
743+
case Mc - Num < 0 orelse
744+
MaxSz - LiveSize < 0 of
726745
true ->
727746
{lists:reverse(Acc), All};
728747
false ->
729-
take_group(Rem, #{max_count => Mc - Num}, [E | Acc])
748+
take_group(Rem, #{max_count => Mc - Num,
749+
max_size => MaxSz - LiveSize},
750+
[E | Acc])
730751
end;
731752
%% skip this secment
732753
false when Acc == [] ->
733-
take_group(Rem, #{max_count => Mc}, Acc);
754+
take_group(Rem, #{max_count => Mc,
755+
max_size => MaxSz}, Acc);
734756
false ->
735757
{lists:reverse(Acc), Rem}
736758
end.

src/ra_server_proc.erl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,8 @@ send_snapshots(Id, Term, {_, ToNode} = To, ChunkSize,
19271927
case ra_snapshot:indexes(ra_snapshot:current_snapshot_dir(SnapState)) of
19281928
{ok, [_|_] = Indexes} ->
19291929
%% there are live indexes to send before the snapshot
1930+
%% %% TODO: only send live indexes higher than the follower's
1931+
%% last_applied index
19301932
Idxs = lists:reverse(ra_seq:expand(Indexes)),
19311933
Flru = lists:foldl(
19321934
fun (Is, F0) ->
@@ -1937,7 +1939,7 @@ send_snapshots(Id, Term, {_, ToNode} = To, ChunkSize,
19371939
data = Ents},
19381940
_Res1 = gen_statem:call(To, RPC1,
19391941
{dirty_timeout, InstallTimeout}),
1940-
%% TODO: assert REs1 is successful
1942+
%% TODO: assert Res1 is successful
19411943
F
19421944
end, undefined, ra_lib:lists_chunk(16, Idxs)),
19431945
_ = ra_flru:evict_all(Flru),

test/coordination_SUITE.erl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,8 +1431,6 @@ snapshot_installed(#{machine_version := _,
14311431

14321432
node_setup(DataDir) ->
14331433
ok = ra_lib:make_dir(DataDir),
1434-
% NodeDir = filename:join(DataDir, atom_to_list(node())),
1435-
% ok = ra_lib:make_dir(DataDir),
14361434
LogFile = filename:join(DataDir, "ra.log"),
14371435
SaslFile = filename:join(DataDir, "ra_sasl.log"),
14381436
logger:set_primary_config(level, debug),

test/ra_log_segment_SUITE.erl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ all_tests() ->
3838
corrupted_segment,
3939
large_segment,
4040
segref,
41+
info,
42+
info_2,
4143
versions_v1,
4244
copy
4345
].
@@ -207,6 +209,39 @@ segref(Config) ->
207209
{<<"seg1.seg">>, {1, 1}} = ra_log_segment:segref(Seg1),
208210
ok.
209211

212+
info(Config) ->
213+
Dir = ?config(data_dir, Config),
214+
Fn = filename:join(Dir, "seg1.seg"),
215+
{ok, Seg0} = ra_log_segment:open(Fn, #{max_count => 128}),
216+
Info1 = ra_log_segment:info(Fn),
217+
?assertMatch(#{ref := undefined}, Info1),
218+
{ok, Seg1} = ra_log_segment:append(Seg0, 1, 2, <<"Adsf">>),
219+
_ = ra_log_segment:flush(Seg1),
220+
Info2 = ra_log_segment:info(Fn),
221+
?assertMatch(#{ref := {<<"seg1.seg">>, {1, 1}}}, Info2),
222+
ok.
223+
224+
info_2(Config) ->
225+
%% passes live indexes which will result in additional info keys
226+
Dir = ?config(data_dir, Config),
227+
Fn = filename:join(Dir, "seg1.seg"),
228+
{ok, Seg0} = ra_log_segment:open(Fn, #{max_count => 128}),
229+
Info1 = ra_log_segment:info(Fn, []),
230+
?assertMatch(#{ref := undefined,
231+
live_size := 0}, Info1),
232+
{ok, Seg1} = ra_log_segment:append(Seg0, 1, 2, <<"Adsf">>),
233+
{ok, Seg2} = ra_log_segment:append(Seg1, 2, 2, <<"Adsf">>),
234+
_ = ra_log_segment:flush(Seg2),
235+
Info2 = ra_log_segment:info(Fn, [1]),
236+
?assertMatch(#{ref := {<<"seg1.seg">>, {1, 2}},
237+
num_entries := 2,
238+
live_size := 4}, Info2),
239+
Info3 = ra_log_segment:info(Fn),
240+
%% info/1 assumes all indexes are "live"
241+
?assertMatch(#{ref := {<<"seg1.seg">>, {1, 2}},
242+
num_entries := 2,
243+
live_size := 8}, Info3),
244+
ok.
210245

211246
full_file(Config) ->
212247
Dir = ?config(data_dir, Config),

0 commit comments

Comments
 (0)