diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index 08a75fb328..10db03991e 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -1001,9 +1001,8 @@ static int prepare_transaction_update(struct write_transaction_table_arg **out, if (!arg) { struct reftable_addition *addition; - ret = reftable_stack_new_addition(&addition, be->stack, - &reftable_be_write_options(refs)->opts, - REFTABLE_STACK_NEW_ADDITION_RELOAD); + ret = reftable_stack_addition_new(&addition, be->stack, + &reftable_be_write_options(refs)->opts); if (ret) { if (ret == REFTABLE_LOCK_ERROR) strbuf_addstr(err, "cannot lock references"); @@ -2009,8 +2008,7 @@ static int reftable_be_rename_ref(struct ref_store *ref_store, if (ret) goto done; ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg, - &reftable_be_write_options(refs)->opts, - REFTABLE_STACK_NEW_ADDITION_RELOAD); + &reftable_be_write_options(refs)->opts); done: assert(ret != REFTABLE_API_ERROR); @@ -2040,8 +2038,7 @@ static int reftable_be_copy_ref(struct ref_store *ref_store, if (ret) goto done; ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg, - &reftable_be_write_options(refs)->opts, - REFTABLE_STACK_NEW_ADDITION_RELOAD); + &reftable_be_write_options(refs)->opts); done: assert(ret != REFTABLE_API_ERROR); @@ -2423,8 +2420,7 @@ static int reftable_be_create_reflog(struct ref_store *ref_store, arg.stack = be->stack; ret = reftable_stack_add(be->stack, &write_reflog_existence_table, &arg, - &reftable_be_write_options(refs)->opts, - REFTABLE_STACK_NEW_ADDITION_RELOAD); + &reftable_be_write_options(refs)->opts); done: return ret; @@ -2498,8 +2494,7 @@ static int reftable_be_delete_reflog(struct ref_store *ref_store, arg.stack = be->stack; ret = reftable_stack_add(be->stack, &write_reflog_delete_table, &arg, - &reftable_be_write_options(refs)->opts, - REFTABLE_STACK_NEW_ADDITION_RELOAD); + &reftable_be_write_options(refs)->opts); assert(ret != REFTABLE_API_ERROR); return ret; @@ -2620,9 +2615,8 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store, if (ret < 0) goto done; - ret = reftable_stack_new_addition(&add, be->stack, - &reftable_be_write_options(refs)->opts, - REFTABLE_STACK_NEW_ADDITION_RELOAD); + ret = reftable_stack_addition_new(&add, be->stack, + &reftable_be_write_options(refs)->opts); if (ret < 0) goto done; diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h index 5d22d84e80..875d09d241 100644 --- a/reftable/reftable-stack.h +++ b/reftable/reftable-stack.h @@ -58,22 +58,13 @@ uint64_t reftable_stack_next_update_index(struct reftable_stack *st); /* holds a transaction to add tables at the top of a stack. */ struct reftable_addition; -enum { - /* - * Reload the stack when the stack is out-of-date after locking it. - */ - REFTABLE_STACK_NEW_ADDITION_RELOAD = (1 << 0), -}; - /* * returns a new transaction to add reftables to the given stack. As a side - * effect, the ref database is locked. Accepts REFTABLE_STACK_NEW_ADDITION_* - * flags. + * effect, the ref database is locked. */ -int reftable_stack_new_addition(struct reftable_addition **dest, +int reftable_stack_addition_new(struct reftable_addition **dest, struct reftable_stack *st, - const struct reftable_write_options *opts, - unsigned int flags); + const struct reftable_write_options *opts); /* Adds a reftable to transaction. */ int reftable_addition_add(struct reftable_addition *add, @@ -93,14 +84,12 @@ void reftable_addition_destroy(struct reftable_addition *add); /* * Add a new table to the stack. The write_table function must call * reftable_writer_set_limits, add refs and return an error value. - * The flags are passed through to `reftable_stack_new_addition()`. */ int reftable_stack_add(struct reftable_stack *st, int (*write_table)(struct reftable_writer *wr, void *write_arg), void *write_arg, - const struct reftable_write_options *opts, - unsigned flags); + const struct reftable_write_options *opts); struct reftable_iterator; diff --git a/reftable/stack.c b/reftable/stack.c index 308f9578f0..47a60db079 100644 --- a/reftable/stack.c +++ b/reftable/stack.c @@ -536,6 +536,8 @@ int reftable_new_stack(struct reftable_stack **dest, const char *dir, goto out; } + p->list_lock = REFTABLE_FLOCK_INIT; + err = reftable_stack_reload_maybe_reuse(p, 1); if (err < 0) goto out; @@ -551,14 +553,21 @@ out: /* * Check whether the given stack is up-to-date with what we have in memory. + * If skip_if_locked is set skip stack reloading if the stack is currently + * locked. Stack reloading must _not_ be skipped right after obtaining the + * lock, to check for concurrent updates which may have happened. + * * Returns 0 if so, 1 if the stack is out-of-date or a negative error code * otherwise. */ -static int stack_uptodate(struct reftable_stack *st) +static int stack_uptodate(struct reftable_stack *st, int skip_if_locked) { char **names = NULL; int err; + if (skip_if_locked && st->list_lock.fd != -1) + return 0; + /* * When we have cached stat information available then we use it to * verify whether the file has been rewritten. @@ -621,17 +630,23 @@ done: int reftable_stack_reload(struct reftable_stack *st) { - int err = stack_uptodate(st); + int err = stack_uptodate(st, 1); if (err > 0) return reftable_stack_reload_maybe_reuse(st, 1); return err; } struct reftable_addition { - struct reftable_flock tables_list_lock; struct reftable_stack *stack; struct reftable_write_options opts; + /* + * While the list lock is acquired on the stack, we need to distinguish + * which 'reftable_addition' is responsible for the lock. This avoids + * clearing the lock of another 'reftable_addition'. + */ + unsigned int locked : 1; + char **new_tables; size_t new_tables_len, new_tables_cap; uint64_t next_update_index; @@ -653,14 +668,15 @@ static void reftable_addition_close(struct reftable_addition *add) add->new_tables_len = 0; add->new_tables_cap = 0; - flock_release(&add->tables_list_lock); + if (add->locked) + flock_release(&add->stack->list_lock); + add->locked = 0; reftable_buf_release(&nm); } static int reftable_stack_init_addition(struct reftable_addition *add, struct reftable_stack *st, - const struct reftable_write_options *opts, - unsigned int flags) + const struct reftable_write_options *opts) { struct reftable_buf lock_file_name = REFTABLE_BUF_INIT; int err; @@ -670,31 +686,28 @@ static int reftable_stack_init_addition(struct reftable_addition *add, if (opts) add->opts = *opts; - err = flock_acquire(&add->tables_list_lock, st->list_file, + err = flock_acquire(&add->stack->list_lock, st->list_file, add->opts.lock_timeout_ms); if (err < 0) goto done; + add->locked = 1; if (add->opts.default_permissions) { - if (chmod(add->tables_list_lock.path, + if (chmod(add->stack->list_lock.path, add->opts.default_permissions) < 0) { err = REFTABLE_IO_ERROR; goto done; } } - err = stack_uptodate(st); + err = stack_uptodate(st, 0); if (err < 0) goto done; - if (err > 0 && flags & REFTABLE_STACK_NEW_ADDITION_RELOAD) { + if (err > 0) { err = reftable_stack_reload_maybe_reuse(add->stack, 1); if (err) goto done; } - if (err > 0) { - err = REFTABLE_OUTDATED_ERROR; - goto done; - } add->next_update_index = reftable_stack_next_update_index(st); done: @@ -708,13 +721,12 @@ static int stack_try_add(struct reftable_stack *st, int (*write_table)(struct reftable_writer *wr, void *arg), void *arg, - const struct reftable_write_options *opts, - unsigned flags) + const struct reftable_write_options *opts) { struct reftable_addition add; int err; - err = reftable_stack_init_addition(&add, st, opts, flags); + err = reftable_stack_init_addition(&add, st, opts); if (err < 0) goto done; @@ -731,17 +743,10 @@ done: int reftable_stack_add(struct reftable_stack *st, int (*write)(struct reftable_writer *wr, void *arg), void *arg, - const struct reftable_write_options *opts, - unsigned flags) + const struct reftable_write_options *opts) { - int err = stack_try_add(st, write, arg, opts, flags); + int err = stack_try_add(st, write, arg, opts); if (err < 0) { - if (err == REFTABLE_OUTDATED_ERROR) { - /* Ignore error return, we want to propagate - REFTABLE_OUTDATED_ERROR. - */ - reftable_stack_reload(st); - } return err; } @@ -787,7 +792,7 @@ int reftable_addition_commit(struct reftable_addition *add) goto done; } - err = reftable_write_data(add->tables_list_lock.fd, + err = reftable_write_data(add->stack->list_lock.fd, table_list.buf, table_list.len); reftable_buf_release(&table_list); if (err < 0) { @@ -795,17 +800,18 @@ int reftable_addition_commit(struct reftable_addition *add) goto done; } - err = fsync(add->tables_list_lock.fd); + err = fsync(add->stack->list_lock.fd); if (err < 0) { err = REFTABLE_IO_ERROR; goto done; } - err = flock_commit(&add->tables_list_lock); + err = flock_commit(&add->stack->list_lock); if (err < 0) { err = REFTABLE_IO_ERROR; goto done; } + add->locked = 0; /* success, no more state to clean up. */ for (i = 0; i < add->new_tables_len; i++) @@ -841,10 +847,9 @@ done: return err; } -int reftable_stack_new_addition(struct reftable_addition **dest, +int reftable_stack_addition_new(struct reftable_addition **dest, struct reftable_stack *st, - const struct reftable_write_options *opts, - unsigned int flags) + const struct reftable_write_options *opts) { int err; @@ -852,7 +857,7 @@ int reftable_stack_new_addition(struct reftable_addition **dest, if (!*dest) return REFTABLE_OUT_OF_MEMORY_ERROR; - err = reftable_stack_init_addition(*dest, st, opts, flags); + err = reftable_stack_init_addition(*dest, st, opts); if (err) { reftable_free(*dest); *dest = NULL; @@ -1202,7 +1207,7 @@ static int stack_compact_range(struct reftable_stack *st, * we could check that relevant tables still exist. But for now it's * good enough to just abort. */ - err = stack_uptodate(st); + err = stack_uptodate(st, 0); if (err < 0) goto done; if (err > 0) { @@ -1321,7 +1326,7 @@ static int stack_compact_range(struct reftable_stack *st, * tables with our compacted version. If they don't, then we need to * abort. */ - err = stack_uptodate(st); + err = stack_uptodate(st, 0); if (err < 0) goto done; if (err > 0) { @@ -1840,12 +1845,7 @@ static int reftable_stack_clean_locked(struct reftable_stack *st) int reftable_stack_clean(struct reftable_stack *st) { struct reftable_addition *add = NULL; - int err = reftable_stack_new_addition(&add, st, NULL, 0); - if (err < 0) { - goto done; - } - - err = reftable_stack_reload(st); + int err = reftable_stack_addition_new(&add, st, NULL); if (err < 0) { goto done; } diff --git a/reftable/stack.h b/reftable/stack.h index f7901e6c6f..52e07ad551 100644 --- a/reftable/stack.h +++ b/reftable/stack.h @@ -10,7 +10,6 @@ #define STACK_H #include "system.h" -#include "reftable-writer.h" #include "reftable-stack.h" struct reftable_stack { @@ -18,6 +17,12 @@ struct reftable_stack { char *list_file; int list_fd; + /* + * Set while an addition holds the stack locked. Used by + * stack_uptodate() to skip reload checks while locked. + */ + struct reftable_flock list_lock; + char *reftable_dir; struct reftable_stack_options opts; diff --git a/t/unit-tests/u-reftable-stack.c b/t/unit-tests/u-reftable-stack.c index e6c1635940..b6f1c6cc52 100644 --- a/t/unit-tests/u-reftable-stack.c +++ b/t/unit-tests/u-reftable-stack.c @@ -127,7 +127,7 @@ static void write_n_ref_tables(struct reftable_stack *st, cl_reftable_set_hash(ref.value.val1, i, REFTABLE_HASH_SHA1); cl_assert_equal_i(reftable_stack_add(st, - &write_test_ref, &ref, &opts, 0), 0); + &write_test_ref, &ref, &opts), 0); } } @@ -168,7 +168,7 @@ void test_reftable_stack__add_one(void) err = reftable_new_stack(&st, dir, NULL); cl_assert(!err); - err = reftable_stack_add(st, write_test_ref, &ref, &opts, 0); + err = reftable_stack_add(st, write_test_ref, &ref, &opts); cl_assert(!err); err = reftable_stack_read_ref(st, ref.refname, &dest); @@ -231,12 +231,9 @@ void test_reftable_stack__uptodate(void) cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0); cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0); cl_assert_equal_i(reftable_stack_add(st1, write_test_ref, - &ref1, NULL, 0), 0); + &ref1, NULL), 0); cl_assert_equal_i(reftable_stack_add(st2, write_test_ref, - &ref2, NULL, 0), REFTABLE_OUTDATED_ERROR); - cl_assert_equal_i(reftable_stack_reload(st2), 0); - cl_assert_equal_i(reftable_stack_add(st2, write_test_ref, - &ref2, NULL, 0), 0); + &ref2, NULL), 0); reftable_stack_destroy(st1); reftable_stack_destroy(st2); clear_dir(dir); @@ -260,7 +257,7 @@ void test_reftable_stack__transaction_api(void) reftable_addition_destroy(add); - cl_assert_equal_i(reftable_stack_new_addition(&add, st, NULL, 0), 0); + cl_assert_equal_i(reftable_stack_addition_new(&add, st, NULL), 0); cl_assert_equal_i(reftable_addition_add(add, write_test_ref, &ref), 0); cl_assert_equal_i(reftable_addition_commit(add), 0); @@ -301,21 +298,17 @@ void test_reftable_stack__transaction_with_reload(void) cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0); cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0); - cl_assert_equal_i(reftable_stack_new_addition(&add, st1, NULL, 0), 0); + cl_assert_equal_i(reftable_stack_addition_new(&add, st1, NULL), 0); cl_assert_equal_i(reftable_addition_add(add, write_test_ref, &refs[0]), 0); cl_assert_equal_i(reftable_addition_commit(add), 0); reftable_addition_destroy(add); /* - * The second stack is now outdated, which we should notice. We do not - * create the addition and lock the stack by default, but allow the - * reload to happen when REFTABLE_STACK_NEW_ADDITION_RELOAD is set. + * The second stack is now outdated, but it should automatically reload it + * with the newer updates. */ - cl_assert_equal_i(reftable_stack_new_addition(&add, st2, NULL, 0), - REFTABLE_OUTDATED_ERROR); - cl_assert_equal_i(reftable_stack_new_addition(&add, st2, NULL, - REFTABLE_STACK_NEW_ADDITION_RELOAD), 0); + cl_assert_equal_i(reftable_stack_addition_new(&add, st2, NULL), 0); cl_assert_equal_i(reftable_addition_add(add, write_test_ref, &refs[1]), 0); cl_assert_equal_i(reftable_addition_commit(add), 0); @@ -362,8 +355,8 @@ void test_reftable_stack__transaction_api_performs_auto_compaction(void) * we can ensure that we indeed honor this setting and have * better control over when exactly auto compaction runs. */ - cl_assert_equal_i(reftable_stack_new_addition(&add, - st, &write_opts, 0), 0); + cl_assert_equal_i(reftable_stack_addition_new(&add, + st, &write_opts), 0); cl_assert_equal_i(reftable_addition_add(add, write_test_ref, &ref), 0); cl_assert_equal_i(reftable_addition_commit(add), 0); @@ -400,7 +393,7 @@ void test_reftable_stack__auto_compaction_fails_gracefully(void) cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &ref, NULL, 0), 0); + &ref, NULL), 0); cl_assert_equal_i(st->merged->tables_len, 1); cl_assert_equal_i(st->stats.attempts, 0); cl_assert_equal_i(st->stats.failures, 0); @@ -418,7 +411,7 @@ void test_reftable_stack__auto_compaction_fails_gracefully(void) write_file_buf(table_path.buf, "", 0); ref.update_index = 2; - err = reftable_stack_add(st, write_test_ref, &ref, NULL, 0); + err = reftable_stack_add(st, write_test_ref, &ref, NULL); cl_assert(!err); cl_assert_equal_i(st->merged->tables_len, 2); cl_assert_equal_i(st->stats.attempts, 1); @@ -453,9 +446,9 @@ void test_reftable_stack__update_index_check(void) cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &ref1, NULL, 0), 0); + &ref1, NULL), 0); cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &ref2, NULL, 0), REFTABLE_API_ERROR); + &ref2, NULL), REFTABLE_API_ERROR); reftable_stack_destroy(st); clear_dir(dir); } @@ -469,7 +462,7 @@ void test_reftable_stack__lock_failure(void) cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) cl_assert_equal_i(reftable_stack_add(st, write_error, - &i, NULL, 0), i); + &i, NULL), i); reftable_stack_destroy(st); clear_dir(dir); @@ -513,7 +506,7 @@ void test_reftable_stack__add(void) for (i = 0; i < N; i++) cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &refs[i], &opts, 0), 0); + &refs[i], &opts), 0); for (i = 0; i < N; i++) { struct write_log_arg arg = { @@ -521,7 +514,7 @@ void test_reftable_stack__add(void) .update_index = reftable_stack_next_update_index(st), }; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, &opts, 0), 0); + &arg, &opts), 0); } cl_assert_equal_i(reftable_stack_compact_all(st, &opts, NULL), 0); @@ -604,7 +597,7 @@ void test_reftable_stack__iterator(void) for (i = 0; i < N; i++) cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &refs[i], NULL, 0), 0); + &refs[i], NULL), 0); for (i = 0; i < N; i++) { struct write_log_arg arg = { @@ -613,7 +606,7 @@ void test_reftable_stack__iterator(void) }; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, NULL, 0), 0); + &arg, NULL), 0); } reftable_stack_init_ref_iterator(st, &it); @@ -685,11 +678,11 @@ void test_reftable_stack__log_normalize(void) input.value.update.message = (char *) "one\ntwo"; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, NULL, 0), REFTABLE_API_ERROR); + &arg, NULL), REFTABLE_API_ERROR); input.value.update.message = (char *) "one"; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, NULL, 0), 0); + &arg, NULL), 0); cl_assert_equal_i(reftable_stack_read_log(st, input.refname, &dest), 0); cl_assert_equal_s(dest.value.update.message, "one\n"); @@ -697,7 +690,7 @@ void test_reftable_stack__log_normalize(void) input.value.update.message = (char *) "two\n"; arg.update_index = 2; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, NULL, 0), 0); + &arg, NULL), 0); cl_assert_equal_i(reftable_stack_read_log(st, input.refname, &dest), 0); cl_assert_equal_s(dest.value.update.message, "two\n"); @@ -747,7 +740,7 @@ void test_reftable_stack__tombstone(void) } for (i = 0; i < N; i++) cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &refs[i], NULL, 0), 0); + &refs[i], NULL), 0); for (i = 0; i < N; i++) { struct write_log_arg arg = { @@ -755,7 +748,7 @@ void test_reftable_stack__tombstone(void) .update_index = reftable_stack_next_update_index(st), }; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, NULL, 0), 0); + &arg, NULL), 0); } cl_assert_equal_i(reftable_stack_read_ref(st, "branch", @@ -801,7 +794,7 @@ void test_reftable_stack__hash_id(void) cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &ref, NULL, 0), 0); + &ref, NULL), 0); /* can't read it with the wrong hash ID. */ cl_assert_equal_i(reftable_new_stack(&st32, dir, @@ -869,7 +862,7 @@ void test_reftable_stack__reflog_expire(void) .update_index = reftable_stack_next_update_index(st), }; cl_assert_equal_i(reftable_stack_add(st, write_test_log, - &arg, NULL, 0), 0); + &arg, NULL), 0); } cl_assert_equal_i(reftable_stack_compact_all(st, NULL, NULL), 0); @@ -908,7 +901,7 @@ void test_reftable_stack__empty_add(void) cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); cl_assert_equal_i(reftable_stack_add(st, write_nothing, - NULL, NULL, 0), 0); + NULL, NULL), 0); cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0); clear_dir(dir); reftable_stack_destroy(st); @@ -947,7 +940,7 @@ void test_reftable_stack__auto_compaction(void) }; snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i); - err = reftable_stack_add(st, write_test_ref, &ref, &opts, 0); + err = reftable_stack_add(st, write_test_ref, &ref, &opts); cl_assert(!err); err = reftable_stack_auto_compact(st, &opts); @@ -983,7 +976,7 @@ void test_reftable_stack__auto_compaction_factor(void) }; xsnprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i); - err = reftable_stack_add(st, &write_test_ref, &ref, &opts, 0); + err = reftable_stack_add(st, &write_test_ref, &ref, &opts); cl_assert(!err); cl_assert(i < 5 || st->merged->tables_len < 5 * fastlogN(i, 5)); @@ -1064,7 +1057,7 @@ void test_reftable_stack__add_performs_auto_compaction(void) ref.refname = buf; cl_assert_equal_i(reftable_stack_add(st, write_test_ref, - &ref, &write_opts, 0), 0); + &ref, &write_opts), 0); /* * The stack length should grow continuously for all runs where @@ -1303,7 +1296,7 @@ void test_reftable_stack__invalid_limit_updates(void) reftable_addition_destroy(add); - cl_assert_equal_i(reftable_stack_new_addition(&add, st, &opts, 0), 0); + cl_assert_equal_i(reftable_stack_addition_new(&add, st, &opts), 0); /* * write_limits_after_ref also updates the update indexes after adding @@ -1317,3 +1310,31 @@ void test_reftable_stack__invalid_limit_updates(void) reftable_stack_destroy(st); clear_dir(dir); } + +void test_reftable_stack__two_additions(void) +{ + struct reftable_stack *st = NULL; + char *dir = get_tmp_dir(__LINE__); + struct reftable_addition *add1 = NULL; + struct reftable_addition *add2 = NULL; + + struct reftable_ref_record ref = { + .refname = (char *) "HEAD", + .update_index = 1, + .value_type = REFTABLE_REF_SYMREF, + .value.symref = (char *) "master", + }; + + cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0); + + cl_assert_equal_i(reftable_stack_addition_new(&add1, st, NULL), 0); + cl_assert_equal_i(reftable_stack_addition_new(&add2, st, NULL), REFTABLE_LOCK_ERROR); + + cl_assert_equal_i(reftable_addition_add(add1, write_test_ref, &ref), 0); + + cl_assert_equal_i(reftable_addition_commit(add1), 0); + + reftable_addition_destroy(add1); + reftable_stack_destroy(st); + clear_dir(dir); +}