Merge branch 'ps/reftable-concurrent-compaction'
The code path for compacting reftable files saw some bugfixes against concurrent operation. * ps/reftable-concurrent-compaction: reftable/stack: fix segfault when reload with reused readers fails reftable/stack: reorder swapping in the reloaded stack contents reftable/reader: keep readers alive during iteration reftable/reader: introduce refcounting reftable/stack: fix broken refnames in `write_n_ref_tables()` reftable/reader: inline `reader_close()` reftable/reader: inline `init_reader()` reftable/reader: rename `reftable_new_reader()` reftable/stack: inline `stack_compact_range_stats()` reftable/blocksource: drop malloc block source
This commit is contained in:
@ -55,26 +55,6 @@ void block_source_from_strbuf(struct reftable_block_source *bs,
|
||||
bs->arg = buf;
|
||||
}
|
||||
|
||||
static void malloc_return_block(void *b UNUSED, struct reftable_block *dest)
|
||||
{
|
||||
if (dest->len)
|
||||
memset(dest->data, 0xff, dest->len);
|
||||
reftable_free(dest->data);
|
||||
}
|
||||
|
||||
static struct reftable_block_source_vtable malloc_vtable = {
|
||||
.return_block = &malloc_return_block,
|
||||
};
|
||||
|
||||
static struct reftable_block_source malloc_block_source_instance = {
|
||||
.ops = &malloc_vtable,
|
||||
};
|
||||
|
||||
struct reftable_block_source malloc_block_source(void)
|
||||
{
|
||||
return malloc_block_source_instance;
|
||||
}
|
||||
|
||||
struct file_block_source {
|
||||
uint64_t size;
|
||||
unsigned char *data;
|
||||
|
@ -17,6 +17,4 @@ struct reftable_block_source;
|
||||
void block_source_from_strbuf(struct reftable_block_source *bs,
|
||||
struct strbuf *buf);
|
||||
|
||||
struct reftable_block_source malloc_block_source(void);
|
||||
|
||||
#endif
|
||||
|
@ -162,58 +162,6 @@ done:
|
||||
return err;
|
||||
}
|
||||
|
||||
int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
|
||||
const char *name)
|
||||
{
|
||||
struct reftable_block footer = { NULL };
|
||||
struct reftable_block header = { NULL };
|
||||
int err = 0;
|
||||
uint64_t file_size = block_source_size(source);
|
||||
|
||||
/* Need +1 to read type of first block. */
|
||||
uint32_t read_size = header_size(2) + 1; /* read v2 because it's larger. */
|
||||
memset(r, 0, sizeof(struct reftable_reader));
|
||||
|
||||
if (read_size > file_size) {
|
||||
err = REFTABLE_FORMAT_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = block_source_read_block(source, &header, 0, read_size);
|
||||
if (err != read_size) {
|
||||
err = REFTABLE_IO_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (memcmp(header.data, "REFT", 4)) {
|
||||
err = REFTABLE_FORMAT_ERROR;
|
||||
goto done;
|
||||
}
|
||||
r->version = header.data[4];
|
||||
if (r->version != 1 && r->version != 2) {
|
||||
err = REFTABLE_FORMAT_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
r->size = file_size - footer_size(r->version);
|
||||
r->source = *source;
|
||||
r->name = xstrdup(name);
|
||||
r->hash_id = 0;
|
||||
|
||||
err = block_source_read_block(source, &footer, r->size,
|
||||
footer_size(r->version));
|
||||
if (err != footer_size(r->version)) {
|
||||
err = REFTABLE_IO_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = parse_footer(r, footer.data, header.data);
|
||||
done:
|
||||
reftable_block_done(&footer);
|
||||
reftable_block_done(&header);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct table_iter {
|
||||
struct reftable_reader *r;
|
||||
uint8_t typ;
|
||||
@ -227,6 +175,7 @@ static int table_iter_init(struct table_iter *ti, struct reftable_reader *r)
|
||||
{
|
||||
struct block_iter bi = BLOCK_ITER_INIT;
|
||||
memset(ti, 0, sizeof(*ti));
|
||||
reftable_reader_incref(r);
|
||||
ti->r = r;
|
||||
ti->bi = bi;
|
||||
return 0;
|
||||
@ -314,6 +263,7 @@ static void table_iter_close(struct table_iter *ti)
|
||||
{
|
||||
table_iter_block_done(ti);
|
||||
block_iter_close(&ti->bi);
|
||||
reftable_reader_decref(ti->r);
|
||||
}
|
||||
|
||||
static int table_iter_next_block(struct table_iter *ti)
|
||||
@ -631,31 +581,90 @@ void reftable_reader_init_log_iterator(struct reftable_reader *r,
|
||||
reader_init_iter(r, it, BLOCK_TYPE_LOG);
|
||||
}
|
||||
|
||||
void reader_close(struct reftable_reader *r)
|
||||
int reftable_reader_new(struct reftable_reader **out,
|
||||
struct reftable_block_source *source, char const *name)
|
||||
{
|
||||
block_source_close(&r->source);
|
||||
FREE_AND_NULL(r->name);
|
||||
struct reftable_block footer = { 0 };
|
||||
struct reftable_block header = { 0 };
|
||||
struct reftable_reader *r;
|
||||
uint64_t file_size = block_source_size(source);
|
||||
uint32_t read_size;
|
||||
int err;
|
||||
|
||||
REFTABLE_CALLOC_ARRAY(r, 1);
|
||||
|
||||
/*
|
||||
* We need one extra byte to read the type of first block. We also
|
||||
* pretend to always be reading v2 of the format because it is larger.
|
||||
*/
|
||||
read_size = header_size(2) + 1;
|
||||
if (read_size > file_size) {
|
||||
err = REFTABLE_FORMAT_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
int reftable_new_reader(struct reftable_reader **p,
|
||||
struct reftable_block_source *src, char const *name)
|
||||
{
|
||||
struct reftable_reader *rd = reftable_calloc(1, sizeof(*rd));
|
||||
int err = init_reader(rd, src, name);
|
||||
if (err == 0) {
|
||||
*p = rd;
|
||||
} else {
|
||||
block_source_close(src);
|
||||
reftable_free(rd);
|
||||
err = block_source_read_block(source, &header, 0, read_size);
|
||||
if (err != read_size) {
|
||||
err = REFTABLE_IO_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (memcmp(header.data, "REFT", 4)) {
|
||||
err = REFTABLE_FORMAT_ERROR;
|
||||
goto done;
|
||||
}
|
||||
r->version = header.data[4];
|
||||
if (r->version != 1 && r->version != 2) {
|
||||
err = REFTABLE_FORMAT_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
r->size = file_size - footer_size(r->version);
|
||||
r->source = *source;
|
||||
r->name = xstrdup(name);
|
||||
r->hash_id = 0;
|
||||
r->refcount = 1;
|
||||
|
||||
err = block_source_read_block(source, &footer, r->size,
|
||||
footer_size(r->version));
|
||||
if (err != footer_size(r->version)) {
|
||||
err = REFTABLE_IO_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = parse_footer(r, footer.data, header.data);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
*out = r;
|
||||
|
||||
done:
|
||||
reftable_block_done(&footer);
|
||||
reftable_block_done(&header);
|
||||
if (err) {
|
||||
reftable_free(r);
|
||||
block_source_close(source);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void reftable_reader_free(struct reftable_reader *r)
|
||||
void reftable_reader_incref(struct reftable_reader *r)
|
||||
{
|
||||
if (!r->refcount)
|
||||
BUG("cannot increment ref counter of dead reader");
|
||||
r->refcount++;
|
||||
}
|
||||
|
||||
void reftable_reader_decref(struct reftable_reader *r)
|
||||
{
|
||||
if (!r)
|
||||
return;
|
||||
reader_close(r);
|
||||
if (!r->refcount)
|
||||
BUG("cannot decrement ref counter of dead reader");
|
||||
if (--r->refcount)
|
||||
return;
|
||||
block_source_close(&r->source);
|
||||
FREE_AND_NULL(r->name);
|
||||
reftable_free(r);
|
||||
}
|
||||
|
||||
@ -786,7 +795,7 @@ int reftable_reader_print_blocks(const char *tablename)
|
||||
if (err < 0)
|
||||
goto done;
|
||||
|
||||
err = reftable_new_reader(&r, &src, tablename);
|
||||
err = reftable_reader_new(&r, &src, tablename);
|
||||
if (err < 0)
|
||||
goto done;
|
||||
|
||||
@ -817,7 +826,7 @@ int reftable_reader_print_blocks(const char *tablename)
|
||||
}
|
||||
|
||||
done:
|
||||
reftable_reader_free(r);
|
||||
reftable_reader_decref(r);
|
||||
table_iter_close(&ti);
|
||||
return err;
|
||||
}
|
||||
|
@ -50,11 +50,10 @@ struct reftable_reader {
|
||||
struct reftable_reader_offsets ref_offsets;
|
||||
struct reftable_reader_offsets obj_offsets;
|
||||
struct reftable_reader_offsets log_offsets;
|
||||
|
||||
uint64_t refcount;
|
||||
};
|
||||
|
||||
int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
|
||||
const char *name);
|
||||
void reader_close(struct reftable_reader *r);
|
||||
const char *reader_name(struct reftable_reader *r);
|
||||
|
||||
void reader_init_iter(struct reftable_reader *r,
|
||||
|
@ -23,16 +23,28 @@
|
||||
/* The reader struct is a handle to an open reftable file. */
|
||||
struct reftable_reader;
|
||||
|
||||
/* reftable_new_reader opens a reftable for reading. If successful,
|
||||
/* reftable_reader_new opens a reftable for reading. If successful,
|
||||
* returns 0 code and sets pp. The name is used for creating a
|
||||
* stack. Typically, it is the basename of the file. The block source
|
||||
* `src` is owned by the reader, and is closed on calling
|
||||
* reftable_reader_destroy(). On error, the block source `src` is
|
||||
* closed as well.
|
||||
*/
|
||||
int reftable_new_reader(struct reftable_reader **pp,
|
||||
int reftable_reader_new(struct reftable_reader **pp,
|
||||
struct reftable_block_source *src, const char *name);
|
||||
|
||||
/*
|
||||
* Manage the reference count of the reftable reader. A newly initialized
|
||||
* reader starts with a refcount of 1 and will be deleted once the refcount has
|
||||
* reached 0.
|
||||
*
|
||||
* This is required because readers may have longer lifetimes than the stack
|
||||
* they belong to. The stack may for example be reloaded while the old tables
|
||||
* are still being accessed by an iterator.
|
||||
*/
|
||||
void reftable_reader_incref(struct reftable_reader *reader);
|
||||
void reftable_reader_decref(struct reftable_reader *reader);
|
||||
|
||||
/* Initialize a reftable iterator for reading refs. */
|
||||
void reftable_reader_init_ref_iterator(struct reftable_reader *r,
|
||||
struct reftable_iterator *it);
|
||||
@ -44,9 +56,6 @@ void reftable_reader_init_log_iterator(struct reftable_reader *r,
|
||||
/* returns the hash ID used in this table. */
|
||||
uint32_t reftable_reader_hash_id(struct reftable_reader *r);
|
||||
|
||||
/* closes and deallocates a reader. */
|
||||
void reftable_reader_free(struct reftable_reader *);
|
||||
|
||||
/* return an iterator for the refs pointing to `oid`. */
|
||||
int reftable_reader_refs_for(struct reftable_reader *r,
|
||||
struct reftable_iterator *it, uint8_t *oid);
|
||||
|
@ -186,7 +186,7 @@ void reftable_stack_destroy(struct reftable_stack *st)
|
||||
if (names && !has_name(names, name)) {
|
||||
stack_filename(&filename, st, name);
|
||||
}
|
||||
reftable_reader_free(st->readers[i]);
|
||||
reftable_reader_decref(st->readers[i]);
|
||||
|
||||
if (filename.len) {
|
||||
/* On Windows, can only unlink after closing. */
|
||||
@ -226,6 +226,8 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
|
||||
{
|
||||
size_t cur_len = !st->merged ? 0 : st->merged->readers_len;
|
||||
struct reftable_reader **cur = stack_copy_readers(st, cur_len);
|
||||
struct reftable_reader **reused = NULL;
|
||||
size_t reused_len = 0, reused_alloc = 0;
|
||||
size_t names_len = names_length(names);
|
||||
struct reftable_reader **new_readers =
|
||||
reftable_calloc(names_len, sizeof(*new_readers));
|
||||
@ -245,6 +247,18 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
|
||||
if (cur[i] && 0 == strcmp(cur[i]->name, name)) {
|
||||
rd = cur[i];
|
||||
cur[i] = NULL;
|
||||
|
||||
/*
|
||||
* When reloading the stack fails, we end up
|
||||
* releasing all new readers. This also
|
||||
* includes the reused readers, even though
|
||||
* they are still in used by the old stack. We
|
||||
* thus need to keep them alive here, which we
|
||||
* do by bumping their refcount.
|
||||
*/
|
||||
REFTABLE_ALLOC_GROW(reused, reused_len + 1, reused_alloc);
|
||||
reused[reused_len++] = rd;
|
||||
reftable_reader_incref(rd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -258,7 +272,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
|
||||
if (err < 0)
|
||||
goto done;
|
||||
|
||||
err = reftable_new_reader(&rd, &src, name);
|
||||
err = reftable_reader_new(&rd, &src, name);
|
||||
if (err < 0)
|
||||
goto done;
|
||||
}
|
||||
@ -273,37 +287,47 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
|
||||
if (err < 0)
|
||||
goto done;
|
||||
|
||||
st->readers_len = new_readers_len;
|
||||
if (st->merged)
|
||||
reftable_merged_table_free(st->merged);
|
||||
if (st->readers) {
|
||||
reftable_free(st->readers);
|
||||
}
|
||||
st->readers = new_readers;
|
||||
new_readers = NULL;
|
||||
new_readers_len = 0;
|
||||
|
||||
new_merged->suppress_deletions = 1;
|
||||
st->merged = new_merged;
|
||||
/*
|
||||
* Close the old, non-reused readers and proactively try to unlink
|
||||
* them. This is done for systems like Windows, where the underlying
|
||||
* file of such an open reader wouldn't have been possible to be
|
||||
* unlinked by the compacting process.
|
||||
*/
|
||||
for (i = 0; i < cur_len; i++) {
|
||||
if (cur[i]) {
|
||||
const char *name = reader_name(cur[i]);
|
||||
stack_filename(&table_path, st, name);
|
||||
|
||||
reader_close(cur[i]);
|
||||
reftable_reader_free(cur[i]);
|
||||
|
||||
/* On Windows, can only unlink after closing. */
|
||||
reftable_reader_decref(cur[i]);
|
||||
unlink(table_path.buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the stack to point to the new tables. */
|
||||
if (st->merged)
|
||||
reftable_merged_table_free(st->merged);
|
||||
new_merged->suppress_deletions = 1;
|
||||
st->merged = new_merged;
|
||||
|
||||
if (st->readers)
|
||||
reftable_free(st->readers);
|
||||
st->readers = new_readers;
|
||||
st->readers_len = new_readers_len;
|
||||
new_readers = NULL;
|
||||
new_readers_len = 0;
|
||||
|
||||
/*
|
||||
* Decrement the refcount of reused readers again. This only needs to
|
||||
* happen on the successful case, because on the unsuccessful one we
|
||||
* decrement their refcount via `new_readers`.
|
||||
*/
|
||||
for (i = 0; i < reused_len; i++)
|
||||
reftable_reader_decref(reused[i]);
|
||||
|
||||
done:
|
||||
for (i = 0; i < new_readers_len; i++) {
|
||||
reader_close(new_readers[i]);
|
||||
reftable_reader_free(new_readers[i]);
|
||||
}
|
||||
for (i = 0; i < new_readers_len; i++)
|
||||
reftable_reader_decref(new_readers[i]);
|
||||
reftable_free(new_readers);
|
||||
reftable_free(reused);
|
||||
reftable_free(cur);
|
||||
strbuf_release(&table_path);
|
||||
return err;
|
||||
@ -1328,17 +1352,9 @@ done:
|
||||
strbuf_release(&table_name);
|
||||
free_names(names);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int stack_compact_range_stats(struct reftable_stack *st,
|
||||
size_t first, size_t last,
|
||||
struct reftable_log_expiry_config *config,
|
||||
unsigned int flags)
|
||||
{
|
||||
int err = stack_compact_range(st, first, last, config, flags);
|
||||
if (err == REFTABLE_LOCK_ERROR)
|
||||
st->stats.failures++;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1346,7 +1362,7 @@ int reftable_stack_compact_all(struct reftable_stack *st,
|
||||
struct reftable_log_expiry_config *config)
|
||||
{
|
||||
size_t last = st->merged->readers_len ? st->merged->readers_len - 1 : 0;
|
||||
return stack_compact_range_stats(st, 0, last, config, 0);
|
||||
return stack_compact_range(st, 0, last, config, 0);
|
||||
}
|
||||
|
||||
static int segment_size(struct segment *s)
|
||||
@ -1452,7 +1468,7 @@ int reftable_stack_auto_compact(struct reftable_stack *st)
|
||||
st->opts.auto_compaction_factor);
|
||||
reftable_free(sizes);
|
||||
if (segment_size(&seg) > 0)
|
||||
return stack_compact_range_stats(st, seg.start, seg.end - 1,
|
||||
return stack_compact_range(st, seg.start, seg.end - 1,
|
||||
NULL, STACK_COMPACT_RANGE_BEST_EFFORT);
|
||||
|
||||
return 0;
|
||||
@ -1540,12 +1556,12 @@ static void remove_maybe_stale_table(struct reftable_stack *st, uint64_t max,
|
||||
if (err < 0)
|
||||
goto done;
|
||||
|
||||
err = reftable_new_reader(&rd, &src, name);
|
||||
err = reftable_reader_new(&rd, &src, name);
|
||||
if (err < 0)
|
||||
goto done;
|
||||
|
||||
update_idx = reftable_reader_max_update_index(rd);
|
||||
reftable_reader_free(rd);
|
||||
reftable_reader_decref(rd);
|
||||
|
||||
if (update_idx <= max) {
|
||||
unlink(table_path.buf);
|
||||
|
@ -10,6 +10,7 @@ https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#include "copy.h"
|
||||
#include "reftable-reader.h"
|
||||
#include "merged.h"
|
||||
#include "basics.h"
|
||||
@ -125,6 +126,7 @@ static void write_n_ref_tables(struct reftable_stack *st,
|
||||
.value_type = REFTABLE_REF_VAL1,
|
||||
};
|
||||
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "refs/heads/branch-%04u", (unsigned) i);
|
||||
ref.refname = buf.buf;
|
||||
set_test_hash(ref.value.val1, i);
|
||||
@ -1035,10 +1037,8 @@ static void test_reftable_stack_compaction_concurrent(void)
|
||||
static void unclean_stack_close(struct reftable_stack *st)
|
||||
{
|
||||
/* break abstraction boundary to simulate unclean shutdown. */
|
||||
int i = 0;
|
||||
for (; i < st->readers_len; i++) {
|
||||
reftable_reader_free(st->readers[i]);
|
||||
}
|
||||
for (size_t i = 0; i < st->readers_len; i++)
|
||||
reftable_reader_decref(st->readers[i]);
|
||||
st->readers_len = 0;
|
||||
FREE_AND_NULL(st->readers);
|
||||
}
|
||||
@ -1077,6 +1077,112 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
|
||||
clear_dir(dir);
|
||||
}
|
||||
|
||||
static void test_reftable_stack_read_across_reload(void)
|
||||
{
|
||||
struct reftable_write_options opts = { 0 };
|
||||
struct reftable_stack *st1 = NULL, *st2 = NULL;
|
||||
struct reftable_ref_record rec = { 0 };
|
||||
struct reftable_iterator it = { 0 };
|
||||
char *dir = get_tmp_dir(__LINE__);
|
||||
int err;
|
||||
|
||||
/* Create a first stack and set up an iterator for it. */
|
||||
err = reftable_new_stack(&st1, dir, &opts);
|
||||
EXPECT_ERR(err);
|
||||
write_n_ref_tables(st1, 2);
|
||||
EXPECT(st1->merged->readers_len == 2);
|
||||
reftable_stack_init_ref_iterator(st1, &it);
|
||||
err = reftable_iterator_seek_ref(&it, "");
|
||||
EXPECT_ERR(err);
|
||||
|
||||
/* Set up a second stack for the same directory and compact it. */
|
||||
err = reftable_new_stack(&st2, dir, &opts);
|
||||
EXPECT_ERR(err);
|
||||
EXPECT(st2->merged->readers_len == 2);
|
||||
err = reftable_stack_compact_all(st2, NULL);
|
||||
EXPECT_ERR(err);
|
||||
EXPECT(st2->merged->readers_len == 1);
|
||||
|
||||
/*
|
||||
* Verify that we can continue to use the old iterator even after we
|
||||
* have reloaded its stack.
|
||||
*/
|
||||
err = reftable_stack_reload(st1);
|
||||
EXPECT_ERR(err);
|
||||
EXPECT(st1->merged->readers_len == 1);
|
||||
err = reftable_iterator_next_ref(&it, &rec);
|
||||
EXPECT_ERR(err);
|
||||
EXPECT(!strcmp(rec.refname, "refs/heads/branch-0000"));
|
||||
err = reftable_iterator_next_ref(&it, &rec);
|
||||
EXPECT_ERR(err);
|
||||
EXPECT(!strcmp(rec.refname, "refs/heads/branch-0001"));
|
||||
err = reftable_iterator_next_ref(&it, &rec);
|
||||
EXPECT(err > 0);
|
||||
|
||||
reftable_ref_record_release(&rec);
|
||||
reftable_iterator_destroy(&it);
|
||||
reftable_stack_destroy(st1);
|
||||
reftable_stack_destroy(st2);
|
||||
clear_dir(dir);
|
||||
}
|
||||
|
||||
static void test_reftable_stack_reload_with_missing_table(void)
|
||||
{
|
||||
struct reftable_write_options opts = { 0 };
|
||||
struct reftable_stack *st = NULL;
|
||||
struct reftable_ref_record rec = { 0 };
|
||||
struct reftable_iterator it = { 0 };
|
||||
struct strbuf table_path = STRBUF_INIT, content = STRBUF_INIT;
|
||||
char *dir = get_tmp_dir(__LINE__);
|
||||
int err;
|
||||
|
||||
/* Create a first stack and set up an iterator for it. */
|
||||
err = reftable_new_stack(&st, dir, &opts);
|
||||
EXPECT_ERR(err);
|
||||
write_n_ref_tables(st, 2);
|
||||
EXPECT(st->merged->readers_len == 2);
|
||||
reftable_stack_init_ref_iterator(st, &it);
|
||||
err = reftable_iterator_seek_ref(&it, "");
|
||||
EXPECT_ERR(err);
|
||||
|
||||
/*
|
||||
* Update the tables.list file with some garbage data, while reusing
|
||||
* our old readers. This should trigger a partial reload of the stack,
|
||||
* where we try to reuse our old readers.
|
||||
*/
|
||||
strbuf_addf(&content, "%s\n", st->readers[0]->name);
|
||||
strbuf_addf(&content, "%s\n", st->readers[1]->name);
|
||||
strbuf_addstr(&content, "garbage\n");
|
||||
strbuf_addf(&table_path, "%s.lock", st->list_file);
|
||||
write_file_buf(table_path.buf, content.buf, content.len);
|
||||
err = rename(table_path.buf, st->list_file);
|
||||
EXPECT_ERR(err);
|
||||
|
||||
err = reftable_stack_reload(st);
|
||||
EXPECT(err == -4);
|
||||
EXPECT(st->merged->readers_len == 2);
|
||||
|
||||
/*
|
||||
* Even though the reload has failed, we should be able to continue
|
||||
* using the iterator.
|
||||
*/
|
||||
err = reftable_iterator_next_ref(&it, &rec);
|
||||
EXPECT_ERR(err);
|
||||
EXPECT(!strcmp(rec.refname, "refs/heads/branch-0000"));
|
||||
err = reftable_iterator_next_ref(&it, &rec);
|
||||
EXPECT_ERR(err);
|
||||
EXPECT(!strcmp(rec.refname, "refs/heads/branch-0001"));
|
||||
err = reftable_iterator_next_ref(&it, &rec);
|
||||
EXPECT(err > 0);
|
||||
|
||||
reftable_ref_record_release(&rec);
|
||||
reftable_iterator_destroy(&it);
|
||||
reftable_stack_destroy(st);
|
||||
strbuf_release(&table_path);
|
||||
strbuf_release(&content);
|
||||
clear_dir(dir);
|
||||
}
|
||||
|
||||
int stack_test_main(int argc UNUSED, const char *argv[] UNUSED)
|
||||
{
|
||||
RUN_TEST(test_empty_add);
|
||||
@ -1099,6 +1205,8 @@ int stack_test_main(int argc UNUSED, const char *argv[] UNUSED)
|
||||
RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
|
||||
RUN_TEST(test_reftable_stack_update_index_check);
|
||||
RUN_TEST(test_reftable_stack_uptodate);
|
||||
RUN_TEST(test_reftable_stack_read_across_reload);
|
||||
RUN_TEST(test_reftable_stack_reload_with_missing_table);
|
||||
RUN_TEST(test_suggest_compaction_segment);
|
||||
RUN_TEST(test_suggest_compaction_segment_nothing);
|
||||
return 0;
|
||||
|
@ -135,7 +135,7 @@ static int dump_reftable(const char *tablename)
|
||||
if (err < 0)
|
||||
goto done;
|
||||
|
||||
err = reftable_new_reader(&r, &src, tablename);
|
||||
err = reftable_reader_new(&r, &src, tablename);
|
||||
if (err < 0)
|
||||
goto done;
|
||||
|
||||
@ -148,7 +148,7 @@ static int dump_reftable(const char *tablename)
|
||||
|
||||
done:
|
||||
reftable_merged_table_free(mt);
|
||||
reftable_reader_free(r);
|
||||
reftable_reader_decref(r);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -29,11 +29,11 @@ static void t_ref_block_read_write(void)
|
||||
int ret;
|
||||
struct block_reader br = { 0 };
|
||||
struct block_iter it = BLOCK_ITER_INIT;
|
||||
struct strbuf want = STRBUF_INIT;
|
||||
struct strbuf want = STRBUF_INIT, buf = STRBUF_INIT;
|
||||
|
||||
REFTABLE_CALLOC_ARRAY(block.data, block_size);
|
||||
block.len = block_size;
|
||||
block.source = malloc_block_source();
|
||||
block_source_from_strbuf(&block.source ,&buf);
|
||||
block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
|
||||
header_off, hash_size(GIT_SHA1_FORMAT_ID));
|
||||
|
||||
@ -99,6 +99,7 @@ static void t_ref_block_read_write(void)
|
||||
reftable_record_release(&rec);
|
||||
reftable_block_done(&br.block);
|
||||
strbuf_release(&want);
|
||||
strbuf_release(&buf);
|
||||
for (i = 0; i < N; i++)
|
||||
reftable_record_release(&recs[i]);
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ merged_table_from_records(struct reftable_ref_record **refs,
|
||||
write_test_table(&buf[i], refs[i], sizes[i]);
|
||||
block_source_from_strbuf(&(*source)[i], &buf[i]);
|
||||
|
||||
err = reftable_new_reader(&(*readers)[i], &(*source)[i],
|
||||
err = reftable_reader_new(&(*readers)[i], &(*source)[i],
|
||||
"name");
|
||||
check(!err);
|
||||
}
|
||||
@ -115,7 +115,7 @@ merged_table_from_records(struct reftable_ref_record **refs,
|
||||
static void readers_destroy(struct reftable_reader **readers, const size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
reftable_reader_free(readers[i]);
|
||||
reftable_reader_decref(readers[i]);
|
||||
reftable_free(readers);
|
||||
}
|
||||
|
||||
@ -277,7 +277,7 @@ merged_table_from_log_records(struct reftable_log_record **logs,
|
||||
write_test_log_table(&buf[i], logs[i], sizes[i], i + 1);
|
||||
block_source_from_strbuf(&(*source)[i], &buf[i]);
|
||||
|
||||
err = reftable_new_reader(&(*readers)[i], &(*source)[i],
|
||||
err = reftable_reader_new(&(*readers)[i], &(*source)[i],
|
||||
"name");
|
||||
check(!err);
|
||||
}
|
||||
@ -426,7 +426,7 @@ static void t_default_write_opts(void)
|
||||
|
||||
block_source_from_strbuf(&source, &buf);
|
||||
|
||||
err = reftable_new_reader(&rd, &source, "filename");
|
||||
err = reftable_reader_new(&rd, &source, "filename");
|
||||
check(!err);
|
||||
|
||||
hash_id = reftable_reader_hash_id(rd);
|
||||
@ -437,7 +437,7 @@ static void t_default_write_opts(void)
|
||||
err = reftable_merged_table_new(&merged, &rd, 1, GIT_SHA1_FORMAT_ID);
|
||||
check(!err);
|
||||
|
||||
reftable_reader_free(rd);
|
||||
reftable_reader_decref(rd);
|
||||
reftable_merged_table_free(merged);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ static void t_log_write_read(void)
|
||||
struct reftable_log_record log = { 0 };
|
||||
int n;
|
||||
struct reftable_iterator it = { 0 };
|
||||
struct reftable_reader rd = { 0 };
|
||||
struct reftable_reader *reader;
|
||||
struct reftable_block_source source = { 0 };
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct reftable_writer *w =
|
||||
@ -246,10 +246,10 @@ static void t_log_write_read(void)
|
||||
|
||||
block_source_from_strbuf(&source, &buf);
|
||||
|
||||
err = init_reader(&rd, &source, "file.log");
|
||||
err = reftable_reader_new(&reader, &source, "file.log");
|
||||
check(!err);
|
||||
|
||||
reftable_reader_init_ref_iterator(&rd, &it);
|
||||
reftable_reader_init_ref_iterator(reader, &it);
|
||||
|
||||
err = reftable_iterator_seek_ref(&it, names[N - 1]);
|
||||
check(!err);
|
||||
@ -264,7 +264,7 @@ static void t_log_write_read(void)
|
||||
reftable_iterator_destroy(&it);
|
||||
reftable_ref_record_release(&ref);
|
||||
|
||||
reftable_reader_init_log_iterator(&rd, &it);
|
||||
reftable_reader_init_log_iterator(reader, &it);
|
||||
|
||||
err = reftable_iterator_seek_log(&it, "");
|
||||
check(!err);
|
||||
@ -285,7 +285,7 @@ static void t_log_write_read(void)
|
||||
/* cleanup. */
|
||||
strbuf_release(&buf);
|
||||
free_names(names);
|
||||
reader_close(&rd);
|
||||
reftable_reader_decref(reader);
|
||||
}
|
||||
|
||||
static void t_log_zlib_corruption(void)
|
||||
@ -294,7 +294,7 @@ static void t_log_zlib_corruption(void)
|
||||
.block_size = 256,
|
||||
};
|
||||
struct reftable_iterator it = { 0 };
|
||||
struct reftable_reader rd = { 0 };
|
||||
struct reftable_reader *reader;
|
||||
struct reftable_block_source source = { 0 };
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct reftable_writer *w =
|
||||
@ -337,18 +337,18 @@ static void t_log_zlib_corruption(void)
|
||||
|
||||
block_source_from_strbuf(&source, &buf);
|
||||
|
||||
err = init_reader(&rd, &source, "file.log");
|
||||
err = reftable_reader_new(&reader, &source, "file.log");
|
||||
check(!err);
|
||||
|
||||
reftable_reader_init_log_iterator(&rd, &it);
|
||||
reftable_reader_init_log_iterator(reader, &it);
|
||||
err = reftable_iterator_seek_log(&it, "refname");
|
||||
check_int(err, ==, REFTABLE_ZLIB_ERROR);
|
||||
|
||||
reftable_iterator_destroy(&it);
|
||||
|
||||
/* cleanup. */
|
||||
reftable_reader_decref(reader);
|
||||
strbuf_release(&buf);
|
||||
reader_close(&rd);
|
||||
}
|
||||
|
||||
static void t_table_read_write_sequential(void)
|
||||
@ -358,7 +358,7 @@ static void t_table_read_write_sequential(void)
|
||||
int N = 50;
|
||||
struct reftable_iterator it = { 0 };
|
||||
struct reftable_block_source source = { 0 };
|
||||
struct reftable_reader rd = { 0 };
|
||||
struct reftable_reader *reader;
|
||||
int err = 0;
|
||||
int j = 0;
|
||||
|
||||
@ -366,10 +366,10 @@ static void t_table_read_write_sequential(void)
|
||||
|
||||
block_source_from_strbuf(&source, &buf);
|
||||
|
||||
err = init_reader(&rd, &source, "file.ref");
|
||||
err = reftable_reader_new(&reader, &source, "file.ref");
|
||||
check(!err);
|
||||
|
||||
reftable_reader_init_ref_iterator(&rd, &it);
|
||||
reftable_reader_init_ref_iterator(reader, &it);
|
||||
err = reftable_iterator_seek_ref(&it, "");
|
||||
check(!err);
|
||||
|
||||
@ -384,11 +384,11 @@ static void t_table_read_write_sequential(void)
|
||||
reftable_ref_record_release(&ref);
|
||||
}
|
||||
check_int(j, ==, N);
|
||||
|
||||
reftable_iterator_destroy(&it);
|
||||
reftable_reader_decref(reader);
|
||||
strbuf_release(&buf);
|
||||
free_names(names);
|
||||
|
||||
reader_close(&rd);
|
||||
}
|
||||
|
||||
static void t_table_write_small_table(void)
|
||||
@ -407,7 +407,7 @@ static void t_table_read_api(void)
|
||||
char **names;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int N = 50;
|
||||
struct reftable_reader rd = { 0 };
|
||||
struct reftable_reader *reader;
|
||||
struct reftable_block_source source = { 0 };
|
||||
int err;
|
||||
struct reftable_log_record log = { 0 };
|
||||
@ -417,10 +417,10 @@ static void t_table_read_api(void)
|
||||
|
||||
block_source_from_strbuf(&source, &buf);
|
||||
|
||||
err = init_reader(&rd, &source, "file.ref");
|
||||
err = reftable_reader_new(&reader, &source, "file.ref");
|
||||
check(!err);
|
||||
|
||||
reftable_reader_init_ref_iterator(&rd, &it);
|
||||
reftable_reader_init_ref_iterator(reader, &it);
|
||||
err = reftable_iterator_seek_ref(&it, names[0]);
|
||||
check(!err);
|
||||
|
||||
@ -430,7 +430,7 @@ static void t_table_read_api(void)
|
||||
strbuf_release(&buf);
|
||||
free_names(names);
|
||||
reftable_iterator_destroy(&it);
|
||||
reader_close(&rd);
|
||||
reftable_reader_decref(reader);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
@ -439,7 +439,7 @@ static void t_table_read_write_seek(int index, int hash_id)
|
||||
char **names;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int N = 50;
|
||||
struct reftable_reader rd = { 0 };
|
||||
struct reftable_reader *reader;
|
||||
struct reftable_block_source source = { 0 };
|
||||
int err;
|
||||
int i = 0;
|
||||
@ -452,17 +452,18 @@ static void t_table_read_write_seek(int index, int hash_id)
|
||||
|
||||
block_source_from_strbuf(&source, &buf);
|
||||
|
||||
err = init_reader(&rd, &source, "file.ref");
|
||||
err = reftable_reader_new(&reader, &source, "file.ref");
|
||||
check(!err);
|
||||
check_int(hash_id, ==, reftable_reader_hash_id(&rd));
|
||||
check_int(hash_id, ==, reftable_reader_hash_id(reader));
|
||||
|
||||
if (!index)
|
||||
rd.ref_offsets.index_offset = 0;
|
||||
else
|
||||
check_int(rd.ref_offsets.index_offset, >, 0);
|
||||
if (!index) {
|
||||
reader->ref_offsets.index_offset = 0;
|
||||
} else {
|
||||
check_int(reader->ref_offsets.index_offset, >, 0);
|
||||
}
|
||||
|
||||
for (i = 1; i < N; i++) {
|
||||
reftable_reader_init_ref_iterator(&rd, &it);
|
||||
reftable_reader_init_ref_iterator(reader, &it);
|
||||
err = reftable_iterator_seek_ref(&it, names[i]);
|
||||
check(!err);
|
||||
err = reftable_iterator_next_ref(&it, &ref);
|
||||
@ -478,7 +479,7 @@ static void t_table_read_write_seek(int index, int hash_id)
|
||||
strbuf_addstr(&pastLast, names[N - 1]);
|
||||
strbuf_addstr(&pastLast, "/");
|
||||
|
||||
reftable_reader_init_ref_iterator(&rd, &it);
|
||||
reftable_reader_init_ref_iterator(reader, &it);
|
||||
err = reftable_iterator_seek_ref(&it, pastLast.buf);
|
||||
if (err == 0) {
|
||||
struct reftable_ref_record ref = { 0 };
|
||||
@ -493,7 +494,7 @@ static void t_table_read_write_seek(int index, int hash_id)
|
||||
|
||||
strbuf_release(&buf);
|
||||
free_names(names);
|
||||
reader_close(&rd);
|
||||
reftable_reader_decref(reader);
|
||||
}
|
||||
|
||||
static void t_table_read_write_seek_linear(void)
|
||||
@ -525,7 +526,7 @@ static void t_table_refs_for(int indexed)
|
||||
int i = 0;
|
||||
int n;
|
||||
int err;
|
||||
struct reftable_reader rd;
|
||||
struct reftable_reader *reader;
|
||||
struct reftable_block_source source = { 0 };
|
||||
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
@ -573,17 +574,17 @@ static void t_table_refs_for(int indexed)
|
||||
|
||||
block_source_from_strbuf(&source, &buf);
|
||||
|
||||
err = init_reader(&rd, &source, "file.ref");
|
||||
err = reftable_reader_new(&reader, &source, "file.ref");
|
||||
check(!err);
|
||||
if (!indexed)
|
||||
rd.obj_offsets.is_present = 0;
|
||||
reader->obj_offsets.is_present = 0;
|
||||
|
||||
reftable_reader_init_ref_iterator(&rd, &it);
|
||||
reftable_reader_init_ref_iterator(reader, &it);
|
||||
err = reftable_iterator_seek_ref(&it, "");
|
||||
check(!err);
|
||||
reftable_iterator_destroy(&it);
|
||||
|
||||
err = reftable_reader_refs_for(&rd, &it, want_hash);
|
||||
err = reftable_reader_refs_for(reader, &it, want_hash);
|
||||
check(!err);
|
||||
|
||||
for (j = 0; ; j++) {
|
||||
@ -600,7 +601,7 @@ static void t_table_refs_for(int indexed)
|
||||
strbuf_release(&buf);
|
||||
free_names(want_names);
|
||||
reftable_iterator_destroy(&it);
|
||||
reader_close(&rd);
|
||||
reftable_reader_decref(reader);
|
||||
}
|
||||
|
||||
static void t_table_refs_for_no_index(void)
|
||||
@ -635,7 +636,7 @@ static void t_write_empty_table(void)
|
||||
|
||||
block_source_from_strbuf(&source, &buf);
|
||||
|
||||
err = reftable_new_reader(&rd, &source, "filename");
|
||||
err = reftable_reader_new(&rd, &source, "filename");
|
||||
check(!err);
|
||||
|
||||
reftable_reader_init_ref_iterator(rd, &it);
|
||||
@ -646,7 +647,7 @@ static void t_write_empty_table(void)
|
||||
check_int(err, >, 0);
|
||||
|
||||
reftable_iterator_destroy(&it);
|
||||
reftable_reader_free(rd);
|
||||
reftable_reader_decref(rd);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
@ -844,7 +845,7 @@ static void t_write_multiple_indices(void)
|
||||
check_int(stats->log_stats.index_offset, >, 0);
|
||||
|
||||
block_source_from_strbuf(&source, &writer_buf);
|
||||
err = reftable_new_reader(&reader, &source, "filename");
|
||||
err = reftable_reader_new(&reader, &source, "filename");
|
||||
check(!err);
|
||||
|
||||
/*
|
||||
@ -857,7 +858,7 @@ static void t_write_multiple_indices(void)
|
||||
|
||||
reftable_iterator_destroy(&it);
|
||||
reftable_writer_free(writer);
|
||||
reftable_reader_free(reader);
|
||||
reftable_reader_decref(reader);
|
||||
strbuf_release(&writer_buf);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
@ -901,7 +902,7 @@ static void t_write_multi_level_index(void)
|
||||
check_int(stats->ref_stats.max_index_level, ==, 2);
|
||||
|
||||
block_source_from_strbuf(&source, &writer_buf);
|
||||
err = reftable_new_reader(&reader, &source, "filename");
|
||||
err = reftable_reader_new(&reader, &source, "filename");
|
||||
check(!err);
|
||||
|
||||
/*
|
||||
@ -913,7 +914,7 @@ static void t_write_multi_level_index(void)
|
||||
|
||||
reftable_iterator_destroy(&it);
|
||||
reftable_writer_free(writer);
|
||||
reftable_reader_free(reader);
|
||||
reftable_reader_decref(reader);
|
||||
strbuf_release(&writer_buf);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
@ -922,11 +923,11 @@ static void t_corrupt_table_empty(void)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct reftable_block_source source = { 0 };
|
||||
struct reftable_reader rd = { 0 };
|
||||
struct reftable_reader *reader;
|
||||
int err;
|
||||
|
||||
block_source_from_strbuf(&source, &buf);
|
||||
err = init_reader(&rd, &source, "file.log");
|
||||
err = reftable_reader_new(&reader, &source, "file.log");
|
||||
check_int(err, ==, REFTABLE_FORMAT_ERROR);
|
||||
}
|
||||
|
||||
@ -935,13 +936,14 @@ static void t_corrupt_table(void)
|
||||
uint8_t zeros[1024] = { 0 };
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct reftable_block_source source = { 0 };
|
||||
struct reftable_reader rd = { 0 };
|
||||
struct reftable_reader *reader;
|
||||
int err;
|
||||
strbuf_add(&buf, zeros, sizeof(zeros));
|
||||
|
||||
block_source_from_strbuf(&source, &buf);
|
||||
err = init_reader(&rd, &source, "file.log");
|
||||
err = reftable_reader_new(&reader, &source, "file.log");
|
||||
check_int(err, ==, REFTABLE_FORMAT_ERROR);
|
||||
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user