Skip to content

Commit e748560

Browse files
pks-tgitster
authored andcommitted
reftable/writer: fix writing multi-level indices
When finishing a section we will potentially write an index that makes it more efficient to look up relevant blocks. The index records written will encode, for each block of the indexed section, what the offset of that block is as well as the last key of that block. Thus, the reader would iterate through the index records to find the first key larger or equal to the wanted key and then use the encoded offset to look up the desired block. When there are a lot of blocks to index though we may end up writing multiple index blocks, too. To not require a linear search across all index blocks we instead end up writing a multi-level index. Instead of referring to the block we are after, an index record may point to another index block. The reader will then access the highest-level index and follow down the chain of index blocks until it hits the sought-after block. It has been observed though that it is impossible to seek ref records of the last ref block when using a multi-level index. While the multi-level index exists and looks fine for most of the part, the highest-level index was missing an index record pointing to the last block of the next index. Thus, every additional level made more refs become unseekable at the end of the ref section. The root cause is that we are not flushing the last block of the current level once done writing the level. Consequently, it wasn't recorded in the blocks that need to be indexed by the next-higher level and thus we forgot about it. Fix this bug by flushing blocks after we have written all index records. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b66e006 commit e748560

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

reftable/readwrite_test.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,61 @@ static void test_write_multiple_indices(void)
866866
strbuf_release(&buf);
867867
}
868868

869+
static void test_write_multi_level_index(void)
870+
{
871+
struct reftable_write_options opts = {
872+
.block_size = 100,
873+
};
874+
struct strbuf writer_buf = STRBUF_INIT, buf = STRBUF_INIT;
875+
struct reftable_block_source source = { 0 };
876+
struct reftable_iterator it = { 0 };
877+
const struct reftable_stats *stats;
878+
struct reftable_writer *writer;
879+
struct reftable_reader *reader;
880+
int err;
881+
882+
writer = reftable_new_writer(&strbuf_add_void, &noop_flush, &writer_buf, &opts);
883+
reftable_writer_set_limits(writer, 1, 1);
884+
for (size_t i = 0; i < 200; i++) {
885+
struct reftable_ref_record ref = {
886+
.update_index = 1,
887+
.value_type = REFTABLE_REF_VAL1,
888+
.value.val1 = {i},
889+
};
890+
891+
strbuf_reset(&buf);
892+
strbuf_addf(&buf, "refs/heads/%03" PRIuMAX, (uintmax_t)i);
893+
ref.refname = buf.buf,
894+
895+
err = reftable_writer_add_ref(writer, &ref);
896+
EXPECT_ERR(err);
897+
}
898+
reftable_writer_close(writer);
899+
900+
/*
901+
* The written refs should be sufficiently large to result in a
902+
* multi-level index.
903+
*/
904+
stats = reftable_writer_stats(writer);
905+
EXPECT(stats->ref_stats.max_index_level == 2);
906+
907+
block_source_from_strbuf(&source, &writer_buf);
908+
err = reftable_new_reader(&reader, &source, "filename");
909+
EXPECT_ERR(err);
910+
911+
/*
912+
* Seeking the last ref should work as expected.
913+
*/
914+
err = reftable_reader_seek_ref(reader, &it, "refs/heads/199");
915+
EXPECT_ERR(err);
916+
917+
reftable_iterator_destroy(&it);
918+
reftable_writer_free(writer);
919+
reftable_reader_free(reader);
920+
strbuf_release(&writer_buf);
921+
strbuf_release(&buf);
922+
}
923+
869924
static void test_corrupt_table_empty(void)
870925
{
871926
struct strbuf buf = STRBUF_INIT;
@@ -916,5 +971,6 @@ int readwrite_test_main(int argc, const char *argv[])
916971
RUN_TEST(test_write_object_id_length);
917972
RUN_TEST(test_write_object_id_min_length);
918973
RUN_TEST(test_write_multiple_indices);
974+
RUN_TEST(test_write_multi_level_index);
919975
return 0;
920976
}

reftable/writer.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -418,15 +418,15 @@ static int writer_finish_section(struct reftable_writer *w)
418418
return err;
419419
}
420420

421+
err = writer_flush_block(w);
422+
if (err < 0)
423+
return err;
424+
421425
for (i = 0; i < idx_len; i++)
422426
strbuf_release(&idx[i].last_key);
423427
reftable_free(idx);
424428
}
425429

426-
err = writer_flush_block(w);
427-
if (err < 0)
428-
return err;
429-
430430
writer_clear_index(w);
431431

432432
bstats = writer_reftable_block_stats(w, typ);

0 commit comments

Comments
 (0)