Skip to content

Commit 82a31ec

Browse files
committed
Merge branch 'jt/reftable-geometric-compaction'
The strategy to compact multiple tables of reftables after many operations accumulate many entries has been improved to avoid accumulating too many tables uncollected. * jt/reftable-geometric-compaction: reftable/stack: use geometric table compaction reftable/stack: add env to disable autocompaction reftable/stack: expose option to disable auto-compaction
2 parents 2b49e41 + a949ebd commit 82a31ec

File tree

6 files changed

+145
-138
lines changed

6 files changed

+145
-138
lines changed

refs/reftable-backend.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "../reftable/reftable-merged.h"
1919
#include "../setup.h"
2020
#include "../strmap.h"
21+
#include "parse.h"
2122
#include "refs-internal.h"
2223

2324
/*
@@ -247,6 +248,8 @@ static struct ref_store *reftable_be_init(struct repository *repo,
247248
refs->write_options.block_size = 4096;
248249
refs->write_options.hash_id = repo->hash_algo->format_id;
249250
refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
251+
refs->write_options.disable_auto_compact =
252+
!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
250253

251254
/*
252255
* Set up the main reftable stack that is hosted in GIT_COMMON_DIR.

reftable/reftable-writer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ struct reftable_write_options {
4646
* is a single line, and add '\n' if missing.
4747
*/
4848
unsigned exact_log_message : 1;
49+
50+
/* boolean: Prevent auto-compaction of tables. */
51+
unsigned disable_auto_compact : 1;
4952
};
5053

5154
/* reftable_block_stats holds statistics for a single block type */

reftable/stack.c

Lines changed: 63 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ int reftable_addition_commit(struct reftable_addition *add)
680680
if (err)
681681
goto done;
682682

683-
if (!add->stack->disable_auto_compact) {
683+
if (!add->stack->config.disable_auto_compact) {
684684
/*
685685
* Auto-compact the stack to keep the number of tables in
686686
* control. It is possible that a concurrent writer is already
@@ -1216,75 +1216,76 @@ static int segment_size(struct segment *s)
12161216
return s->end - s->start;
12171217
}
12181218

1219-
int fastlog2(uint64_t sz)
1220-
{
1221-
int l = 0;
1222-
if (sz == 0)
1223-
return 0;
1224-
for (; sz; sz /= 2) {
1225-
l++;
1226-
}
1227-
return l - 1;
1228-
}
1229-
1230-
struct segment *sizes_to_segments(size_t *seglen, uint64_t *sizes, size_t n)
1231-
{
1232-
struct segment *segs = reftable_calloc(n, sizeof(*segs));
1233-
struct segment cur = { 0 };
1234-
size_t next = 0, i;
1235-
1236-
if (n == 0) {
1237-
*seglen = 0;
1238-
return segs;
1239-
}
1240-
for (i = 0; i < n; i++) {
1241-
int log = fastlog2(sizes[i]);
1242-
if (cur.log != log && cur.bytes > 0) {
1243-
struct segment fresh = {
1244-
.start = i,
1245-
};
1246-
1247-
segs[next++] = cur;
1248-
cur = fresh;
1249-
}
1250-
1251-
cur.log = log;
1252-
cur.end = i + 1;
1253-
cur.bytes += sizes[i];
1254-
}
1255-
segs[next++] = cur;
1256-
*seglen = next;
1257-
return segs;
1258-
}
1259-
12601219
struct segment suggest_compaction_segment(uint64_t *sizes, size_t n)
12611220
{
1262-
struct segment min_seg = {
1263-
.log = 64,
1264-
};
1265-
struct segment *segs;
1266-
size_t seglen = 0, i;
1267-
1268-
segs = sizes_to_segments(&seglen, sizes, n);
1269-
for (i = 0; i < seglen; i++) {
1270-
if (segment_size(&segs[i]) == 1)
1271-
continue;
1221+
struct segment seg = { 0 };
1222+
uint64_t bytes;
1223+
size_t i;
12721224

1273-
if (segs[i].log < min_seg.log)
1274-
min_seg = segs[i];
1275-
}
1225+
/*
1226+
* If there are no tables or only a single one then we don't have to
1227+
* compact anything. The sequence is geometric by definition already.
1228+
*/
1229+
if (n <= 1)
1230+
return seg;
12761231

1277-
while (min_seg.start > 0) {
1278-
size_t prev = min_seg.start - 1;
1279-
if (fastlog2(min_seg.bytes) < fastlog2(sizes[prev]))
1232+
/*
1233+
* Find the ending table of the compaction segment needed to restore the
1234+
* geometric sequence. Note that the segment end is exclusive.
1235+
*
1236+
* To do so, we iterate backwards starting from the most recent table
1237+
* until a valid segment end is found. If the preceding table is smaller
1238+
* than the current table multiplied by the geometric factor (2), the
1239+
* compaction segment end has been identified.
1240+
*
1241+
* Tables after the ending point are not added to the byte count because
1242+
* they are already valid members of the geometric sequence. Due to the
1243+
* properties of a geometric sequence, it is not possible for the sum of
1244+
* these tables to exceed the value of the ending point table.
1245+
*
1246+
* Example table size sequence requiring no compaction:
1247+
* 64, 32, 16, 8, 4, 2, 1
1248+
*
1249+
* Example table size sequence where compaction segment end is set to
1250+
* the last table. Since the segment end is exclusive, the last table is
1251+
* excluded during subsequent compaction and the table with size 3 is
1252+
* the final table included:
1253+
* 64, 32, 16, 8, 4, 3, 1
1254+
*/
1255+
for (i = n - 1; i > 0; i--) {
1256+
if (sizes[i - 1] < sizes[i] * 2) {
1257+
seg.end = i + 1;
1258+
bytes = sizes[i];
12801259
break;
1260+
}
1261+
}
12811262

1282-
min_seg.start = prev;
1283-
min_seg.bytes += sizes[prev];
1263+
/*
1264+
* Find the starting table of the compaction segment by iterating
1265+
* through the remaining tables and keeping track of the accumulated
1266+
* size of all tables seen from the segment end table. The previous
1267+
* table is compared to the accumulated size because the tables from the
1268+
* segment end are merged backwards recursively.
1269+
*
1270+
* Note that we keep iterating even after we have found the first
1271+
* starting point. This is because there may be tables in the stack
1272+
* preceding that first starting point which violate the geometric
1273+
* sequence.
1274+
*
1275+
* Example compaction segment start set to table with size 32:
1276+
* 128, 32, 16, 8, 4, 3, 1
1277+
*/
1278+
for (; i > 0; i--) {
1279+
uint64_t curr = bytes;
1280+
bytes += sizes[i - 1];
1281+
1282+
if (sizes[i - 1] < curr * 2) {
1283+
seg.start = i - 1;
1284+
seg.bytes = bytes;
1285+
}
12841286
}
12851287

1286-
reftable_free(segs);
1287-
return min_seg;
1288+
return seg;
12881289
}
12891290

12901291
static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)

reftable/stack.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ struct reftable_stack {
1919
int list_fd;
2020

2121
char *reftable_dir;
22-
int disable_auto_compact;
2322

2423
struct reftable_write_options config;
2524

@@ -33,12 +32,9 @@ int read_lines(const char *filename, char ***lines);
3332

3433
struct segment {
3534
size_t start, end;
36-
int log;
3735
uint64_t bytes;
3836
};
3937

40-
int fastlog2(uint64_t sz);
41-
struct segment *sizes_to_segments(size_t *seglen, uint64_t *sizes, size_t n);
4238
struct segment suggest_compaction_segment(uint64_t *sizes, size_t n);
4339

4440
#endif

reftable/stack_test.c

Lines changed: 19 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
325325
* we can ensure that we indeed honor this setting and have
326326
* better control over when exactly auto compaction runs.
327327
*/
328-
st->disable_auto_compact = i != n;
328+
st->config.disable_auto_compact = i != n;
329329

330330
err = reftable_stack_new_addition(&add, st);
331331
EXPECT_ERR(err);
@@ -497,6 +497,7 @@ static void test_reftable_stack_add(void)
497497
struct reftable_write_options cfg = {
498498
.exact_log_message = 1,
499499
.default_permissions = 0660,
500+
.disable_auto_compact = 1,
500501
};
501502
struct reftable_stack *st = NULL;
502503
char *dir = get_tmp_dir(__LINE__);
@@ -508,7 +509,6 @@ static void test_reftable_stack_add(void)
508509

509510
err = reftable_new_stack(&st, dir, cfg);
510511
EXPECT_ERR(err);
511-
st->disable_auto_compact = 1;
512512

513513
for (i = 0; i < N; i++) {
514514
char buf[256];
@@ -770,59 +770,13 @@ static void test_reftable_stack_hash_id(void)
770770
clear_dir(dir);
771771
}
772772

773-
static void test_log2(void)
774-
{
775-
EXPECT(1 == fastlog2(3));
776-
EXPECT(2 == fastlog2(4));
777-
EXPECT(2 == fastlog2(5));
778-
}
779-
780-
static void test_sizes_to_segments(void)
781-
{
782-
uint64_t sizes[] = { 2, 3, 4, 5, 7, 9 };
783-
/* .................0 1 2 3 4 5 */
784-
785-
size_t seglen = 0;
786-
struct segment *segs =
787-
sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
788-
EXPECT(segs[2].log == 3);
789-
EXPECT(segs[2].start == 5);
790-
EXPECT(segs[2].end == 6);
791-
792-
EXPECT(segs[1].log == 2);
793-
EXPECT(segs[1].start == 2);
794-
EXPECT(segs[1].end == 5);
795-
reftable_free(segs);
796-
}
797-
798-
static void test_sizes_to_segments_empty(void)
799-
{
800-
size_t seglen = 0;
801-
struct segment *segs = sizes_to_segments(&seglen, NULL, 0);
802-
EXPECT(seglen == 0);
803-
reftable_free(segs);
804-
}
805-
806-
static void test_sizes_to_segments_all_equal(void)
807-
{
808-
uint64_t sizes[] = { 5, 5 };
809-
size_t seglen = 0;
810-
struct segment *segs =
811-
sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
812-
EXPECT(seglen == 1);
813-
EXPECT(segs[0].start == 0);
814-
EXPECT(segs[0].end == 2);
815-
reftable_free(segs);
816-
}
817-
818773
static void test_suggest_compaction_segment(void)
819774
{
820-
uint64_t sizes[] = { 128, 64, 17, 16, 9, 9, 9, 16, 16 };
821-
/* .................0 1 2 3 4 5 6 */
775+
uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
822776
struct segment min =
823777
suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
824-
EXPECT(min.start == 2);
825-
EXPECT(min.end == 7);
778+
EXPECT(min.start == 1);
779+
EXPECT(min.end == 10);
826780
}
827781

828782
static void test_suggest_compaction_segment_nothing(void)
@@ -933,9 +887,21 @@ static void test_empty_add(void)
933887
reftable_stack_destroy(st2);
934888
}
935889

890+
static int fastlog2(uint64_t sz)
891+
{
892+
int l = 0;
893+
if (sz == 0)
894+
return 0;
895+
for (; sz; sz /= 2)
896+
l++;
897+
return l - 1;
898+
}
899+
936900
static void test_reftable_stack_auto_compaction(void)
937901
{
938-
struct reftable_write_options cfg = { 0 };
902+
struct reftable_write_options cfg = {
903+
.disable_auto_compact = 1,
904+
};
939905
struct reftable_stack *st = NULL;
940906
char *dir = get_tmp_dir(__LINE__);
941907

@@ -945,7 +911,6 @@ static void test_reftable_stack_auto_compaction(void)
945911
err = reftable_new_stack(&st, dir, cfg);
946912
EXPECT_ERR(err);
947913

948-
st->disable_auto_compact = 1; /* call manually below for coverage. */
949914
for (i = 0; i < N; i++) {
950915
char name[100];
951916
struct reftable_ref_record ref = {
@@ -994,7 +959,7 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
994959
* we can ensure that we indeed honor this setting and have
995960
* better control over when exactly auto compaction runs.
996961
*/
997-
st->disable_auto_compact = i != n;
962+
st->config.disable_auto_compact = i != n;
998963

999964
strbuf_reset(&refname);
1000965
strbuf_addf(&refname, "branch-%04d", i);
@@ -1121,7 +1086,6 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
11211086
int stack_test_main(int argc, const char *argv[])
11221087
{
11231088
RUN_TEST(test_empty_add);
1124-
RUN_TEST(test_log2);
11251089
RUN_TEST(test_names_equal);
11261090
RUN_TEST(test_parse_names);
11271091
RUN_TEST(test_read_file);
@@ -1142,9 +1106,6 @@ int stack_test_main(int argc, const char *argv[])
11421106
RUN_TEST(test_reftable_stack_update_index_check);
11431107
RUN_TEST(test_reftable_stack_uptodate);
11441108
RUN_TEST(test_reftable_stack_validate_refname);
1145-
RUN_TEST(test_sizes_to_segments);
1146-
RUN_TEST(test_sizes_to_segments_all_equal);
1147-
RUN_TEST(test_sizes_to_segments_empty);
11481109
RUN_TEST(test_suggest_compaction_segment);
11491110
RUN_TEST(test_suggest_compaction_segment_nothing);
11501111
return 0;

0 commit comments

Comments
 (0)