aboutsummaryrefslogtreecommitdiff
path: root/reftable
AgeCommit message (Collapse)Author
2024-09-08t: move reftable/stack_test.c to the unit testing frameworkChandra Pratap
reftable/stack_test.c exercises the functions defined in reftable/stack.{c, h}. Migrate reftable/stack_test.c to the unit testing framework. Migration involves refactoring the tests to use the unit testing framework instead of reftable's test framework and renaming the tests to be in-line with unit-tests' standards. Since some of the tests use set_test_hash() defined by reftable/test_framework.{c, h} but these files are not '#included' in the test file, copy this function in the ported test file. With the migration of stack test to the unit-tests framework, "test-tool reftable" becomes a no-op. Hence, get rid of everything that uses "test-tool reftable" alongside everything that is used to implement it. While at it, alphabetically sort the cmds[] list in helper/test-tool.c by moving the entry for "dump-reftable". Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Chandra Pratap <chandrapratap3519@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-09-03Merge branch 'ps/reftable-concurrent-compaction'Junio C Hamano
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
2024-08-29Merge branch 'cp/unit-test-reftable-block'Junio C Hamano
Another test for reftable library ported to the unit test framework. * cp/unit-test-reftable-block: t-reftable-block: mark unused argv/argc t-reftable-block: add tests for index blocks t-reftable-block: add tests for obj blocks t-reftable-block: add tests for log blocks t-reftable-block: remove unnecessary variable 'j' t-reftable-block: use xstrfmt() instead of xstrdup() t-reftable-block: use block_iter_reset() instead of block_iter_close() t-reftable-block: use reftable_record_key() instead of strbuf_addstr() t-reftable-block: use reftable_record_equal() instead of check_str() t-reftable-block: release used block reader t: harmonize t-reftable-block.c with coding guidelines t: move reftable/block_test.c to the unit testing framework
2024-08-29Merge branch 'ps/reftable-drop-generic'Junio C Hamano
The code in the reftable library has been cleaned up by discarding unused "generic" interface. * ps/reftable-drop-generic: reftable: mark unused parameters in empty iterator functions reftable/generic: drop interface t/helper: refactor to not use `struct reftable_table` t/helper: use `hash_to_hex_algop()` to print hashes t/helper: inline printing of reftable records t/helper: inline `reftable_table_print()` t/helper: inline `reftable_stack_print_directory()` t/helper: inline `reftable_reader_print_file()` t/helper: inline `reftable_dump_main()` reftable/dump: drop unused `compact_stack()` reftable/generic: move generic iterator code into iterator interface reftable/iter: drop double-checking logic reftable/stack: open-code reading refs reftable/merged: stop using generic tables in the merged table reftable/merged: rename `reftable_new_merged_table()` reftable/merged: expose functions to initialize iterators
2024-08-28reftable: mark unused parameters in empty iterator functionsJeff King
These unused parameters were marked in a68ec8683a (reftable: mark unused parameters in virtual functions, 2024-08-17), but the functions were moved to a new file in a parallel branch via f2406c81b9 (reftable/generic: move generic iterator code into iterator interface, 2024-08-22). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-26Merge branch 'jk/mark-unused-parameters'Junio C Hamano
Mark unused parameters as UNUSED to squelch -Wunused warnings. * jk/mark-unused-parameters: t-hashmap: stop calling setup() for t_intern() test scalar: mark unused parameters in dummy function daemon: mark unused parameters in non-posix fallbacks setup: mark unused parameter in config callback test-mergesort: mark unused parameters in trivial callback t-hashmap: mark unused parameters in callback function reftable: mark unused parameters in virtual functions reftable: drop obsolete test function declarations reftable: ignore unused argc/argv in test functions unit-tests: ignore unused argc/argv t/helper: mark more unused argv/argc arguments oss-fuzz: mark unused argv/argc argument refs: mark unused parameters in do_for_each_reflog_helper() refs: mark unused parameters in ref_store fsck callbacks update-ref: mark more unused parameters in parser callbacks imap-send: mark unused parameter in ssl_socket_connect() fallback
2024-08-23Merge branch 'cp/unit-test-reftable-readwrite'Junio C Hamano
* cp/unit-test-reftable-readwrite: t-reftable-readwrite: add test for known error t-reftable-readwrite: use 'for' in place of infinite 'while' loops t-reftable-readwrite: use free_names() instead of a for loop t: move reftable/readwrite_test.c to the unit testing framework
2024-08-23reftable/stack: fix segfault when reload with reused readers failsPatrick Steinhardt
It is expected that reloading the stack fails with concurrent writers, e.g. because a table that we just wanted to read just got compacted. In case we decided to reuse readers this will cause a segfault though because we unconditionally release all new readers, including the reused ones. As those are still referenced by the current stack, the result is that we will eventually try to dereference those already-freed readers. Fix this bug by incrementing the refcount of reused readers temporarily. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-23reftable/stack: reorder swapping in the reloaded stack contentsPatrick Steinhardt
The code flow of how we swap in the reloaded stack contents is somewhat convoluted because we switch back and forth between swapping in different parts of the stack. Reorder the code to simplify it. We now first close and unlink the old tables which do not get reused before we update the stack to point to the new stack. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-23reftable/reader: keep readers alive during iterationPatrick Steinhardt
The lifetime of a table iterator may survive the lifetime of a reader when the stack gets reloaded. Keep the reader from being released by increasing its refcount while the iterator is still being used. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-23reftable/reader: introduce refcountingPatrick Steinhardt
It was recently reported that concurrent reads and writes may cause the reftable backend to segfault. The root cause of this is that we do not properly keep track of reftable readers across reloads. Suppose that you have a reftable iterator and then decide to reload the stack while iterating through the iterator. When the stack has been rewritten since we have created the iterator, then we would end up discarding a subset of readers that may still be in use by the iterator. The consequence is that we now try to reference deallocated memory, which of course segfaults. One way to trigger this is in t5616, where some background maintenance jobs have been leaking from one test into another. This leads to stack traces like the following one: + git -c protocol.version=0 -C pc1 fetch --filter=blob:limit=29999 --refetch origin AddressSanitizer:DEADLYSIGNAL ================================================================= ==657994==ERROR: AddressSanitizer: SEGV on unknown address 0x7fa0f0ec6089 (pc 0x55f23e52ddf9 bp 0x7ffe7bfa1700 sp 0x7ffe7bfa1700 T0) ==657994==The signal is caused by a READ memory access. #0 0x55f23e52ddf9 in get_var_int reftable/record.c:29 #1 0x55f23e53295e in reftable_decode_keylen reftable/record.c:170 #2 0x55f23e532cc0 in reftable_decode_key reftable/record.c:194 #3 0x55f23e54e72e in block_iter_next reftable/block.c:398 #4 0x55f23e5573dc in table_iter_next_in_block reftable/reader.c:240 #5 0x55f23e5573dc in table_iter_next reftable/reader.c:355 #6 0x55f23e5573dc in table_iter_next reftable/reader.c:339 #7 0x55f23e551283 in merged_iter_advance_subiter reftable/merged.c:69 #8 0x55f23e55169e in merged_iter_next_entry reftable/merged.c:123 #9 0x55f23e55169e in merged_iter_next_void reftable/merged.c:172 #10 0x55f23e537625 in reftable_iterator_next_ref reftable/generic.c:175 #11 0x55f23e2cf9c6 in reftable_ref_iterator_advance refs/reftable-backend.c:464 #12 0x55f23e2d996e in ref_iterator_advance refs/iterator.c:13 #13 0x55f23e2d996e in do_for_each_ref_iterator refs/iterator.c:452 #14 0x55f23dca6767 in get_ref_map builtin/fetch.c:623 #15 0x55f23dca6767 in do_fetch builtin/fetch.c:1659 #16 0x55f23dca6767 in fetch_one builtin/fetch.c:2133 #17 0x55f23dca6767 in cmd_fetch builtin/fetch.c:2432 #18 0x55f23dba7764 in run_builtin git.c:484 #19 0x55f23dba7764 in handle_builtin git.c:741 #20 0x55f23dbab61e in run_argv git.c:805 #21 0x55f23dbab61e in cmd_main git.c:1000 #22 0x55f23dba4781 in main common-main.c:64 #23 0x7fa0f063fc89 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #24 0x7fa0f063fd44 in __libc_start_main_impl ../csu/libc-start.c:360 #25 0x55f23dba6ad0 in _start (git+0xadfad0) (BuildId: 803b2b7f59beb03d7849fb8294a8e2145dd4aa27) While it is somewhat awkward that the maintenance processes survive tests in the first place, it is totally expected that reftables should work alright with concurrent writers. Seemingly they don't. The only underlying resource that we need to care about in this context is the reftable reader, which is responsible for reading a single table from disk. These readers get discarded immediately (unless reused) when calling `reftable_stack_reload()`, which is wrong. We can only close them once we know that there are no iterators using them anymore. Prepare for a fix by converting the reftable readers to be refcounted. Reported-by: Jeff King <peff@peff.net> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-23reftable/stack: fix broken refnames in `write_n_ref_tables()`Patrick Steinhardt
The `write_n_ref_tables()` helper function writes N references in separate tables. We never reset the computed name of those references though, leading us to end up with unexpected names. Fix this by resetting the buffer. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-23reftable/reader: inline `reader_close()`Patrick Steinhardt
Same as with the preceding commit, we also provide a `reader_close()` function that allows the caller to close a reader without freeing it. This is unnecessary now that all users will have an allocated version of the reader. Inline it into `reftable_reader_free()`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-23reftable/reader: inline `init_reader()`Patrick Steinhardt
Most users use an allocated version of the `reftable_reader`, except for some tests. We are about to convert the reader to become refcounted though, and providing the ability to keep a reader on the stack makes this conversion harder than necessary. Update the tests to use `reftable_reader_new()` instead to prepare for this change. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-23reftable/reader: rename `reftable_new_reader()`Patrick Steinhardt
Rename the `reftable_new_reader()` function to `reftable_reader_new()` to match our coding guidelines. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-23reftable/stack: inline `stack_compact_range_stats()`Patrick Steinhardt
The only difference between `stack_compact_range_stats()` and `stack_compact_range()` is that the former updates stats on failure, whereas the latter doesn't. There are no callers anymore that do not want their stats updated though, making the indirection unnecessary. Inline the stat updates into `stack_compact_range()`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-23reftable/blocksource: drop malloc block sourcePatrick Steinhardt
The reftable blocksource provides a generic interface to read blocks via different sources, e.g. from disk or from memory. One of the block sources is the malloc block source, which can in theory read data from memory. We nowadays also have a strbuf block source though, which provides essentially the same functionality with better ergonomics. Adapt the only remaining user of the malloc block source in our tests to use the strbuf block source, instead, and remove the now-unused malloc block source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22reftable/generic: drop interfacePatrick Steinhardt
The `reftable_table` interface provides a generic infrastructure that can abstract away whether the underlying table is a single table, or a merged table. This abstraction can make it rather hard to reason about the code. We didn't ever use it to implement the reftable backend, and with the preceding patches in this patch series we in fact don't use it at all anymore. Furthermore, it became somewhat useless with the recent refactorings that made it possible to seek reftable iterators multiple times, as these now provide generic access to tables for us. The interface is thus redundant and only brings unnecessary complexity with it. Remove the `struct reftable_table` interface and its associated functions. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22t/helper: inline printing of reftable recordsPatrick Steinhardt
Move printing of reftable records into the "dump-reftable" helper. This follows the same reasoning as the preceding commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22t/helper: inline `reftable_table_print()`Patrick Steinhardt
Move `reftable_table_print()` into the "dump-reftable" helper. This follows the same reasoning as the preceding commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22t/helper: inline `reftable_stack_print_directory()`Patrick Steinhardt
Move `reftable_stack_print_directory()` into the "dump-reftable" helper. This follows the same reasoning as the preceding commit. Note that this requires us to remove the tests for this functionality in `reftable/stack_test.c`. The test does not really add much anyway, because all it verifies is that we do not crash or run into an error, and it specifically doesn't check the outputted data. Also, as the code is now part of the test helper, it doesn't make much sense to have a unit test for it in the first place. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22t/helper: inline `reftable_reader_print_file()`Patrick Steinhardt
Move `reftable_reader_print_file()` into the "dump-reftable" helper. This follows the same reasoning as the preceding commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22t/helper: inline `reftable_dump_main()`Patrick Steinhardt
The printing functionality part of `reftable/dump.c` is really only used by our "dump-reftable" test helper. It is certainly not generic logic that is useful to anybody outside of Git, and the format it generates is quite specific. Still, parts of it are used in our test suite and the output may be useful to take a peek into reftable stacks, tables and blocks. So while it does not make sense to expose this as part of the reftable library, it does make sense to keep it around. Inline the `reftable_dump_main()` function into the "dump-reftable" test helper. This clarifies that its format is subject to change and not part of our public interface. Furthermore, this allows us to iterate on the implementation in subsequent patches. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22reftable/dump: drop unused `compact_stack()`Patrick Steinhardt
The `compact_stack()` function is exposed via `reftable_dump_main()`, which ultimately ends up being wired into "test-tool reftable". It is never used by our tests though, and nowadays we have wired up support for stack compaction into git-pack-refs(1). Remove the code. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22reftable/generic: move generic iterator code into iterator interfacePatrick Steinhardt
Move functions relating to the reftable iterator from "generic.c" into "iter.c". This prepares for the removal of the former subsystem. While at it, remove some unneeded braces to conform to our coding style. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22reftable/iter: drop double-checking logicPatrick Steinhardt
The filtering ref iterator can be used to only yield refs which are not in a specific skip list. This iterator has an option to double-check the results it returns, which causes us to seek the reference we are about to yield via a separate table such that we detect whether the reference that the first iterator has yielded actually exists. The value of this is somewhat dubious, and I cannot think of any usecase where this functionality should be required. Furthermore, this option is never set in our codebase, which means that it is essentially untested. And last but not least, the `struct reftable_table` that is used to implement it is about to go away. So while we could refactor the code to not use a `reftable_table`, it very much feels like a wasted effort. Let's just drop this code. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22reftable/stack: open-code reading refsPatrick Steinhardt
To read a reference for the reftable stack, we first create a generic `reftable_table` from the merged table and then read the reference via a convenience function. We are about to remove these generic interfaces, so let's instead open-code the logic to prepare for this removal. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22reftable/merged: stop using generic tables in the merged tablePatrick Steinhardt
The merged table provides access to a reftable stack by merging the contents of those tables into a virtual table. These subtables are being tracked via `struct reftable_table`, which is a generic interface for accessing either a single reftable or a merged reftable. So in theory, it would be possible for the merged table to merge together other merged tables. This is somewhat nonsensical though: we only ever set up a merged table over normal reftables, and there is no reason to do otherwise. This generic interface thus makes the code way harder to follow and reason about than really necessary. The abstraction layer may also have an impact on performance, even though the extra set of vtable function calls probably doesn't really matter. Refactor the merged tables to use a `struct reftable_reader` for each of the subtables instead, which gives us direct access to the underlying tables. Adjust names accordingly. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22reftable/merged: rename `reftable_new_merged_table()`Patrick Steinhardt
Rename `reftable_new_merged_table()` to `reftable_merged_table_new()` such that the name matches our coding style. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-22reftable/merged: expose functions to initialize iteratorsPatrick Steinhardt
We do not expose any functions via our public headers that would allow a caller to initialize a reftable iterator from a merged table. Instead, they are expected to go via the generic `reftable_table` interface, which is somewhat roundabout. Implement two new functions to initialize iterators for ref and log records to plug this gap. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-21t: move reftable/block_test.c to the unit testing frameworkChandra Pratap
reftable/block_test.c exercises the functions defined in reftable/block.{c, h}. Migrate reftable/block_test.c to the unit testing framework. Migration involves refactoring the tests to use the unit testing framework instead of reftable's test framework and renaming the tests to follow the unit-tests' naming conventions. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Chandra Pratap <chandrapratap3519@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-17reftable: mark unused parameters in virtual functionsJeff King
The reftable code uses a lot of virtual function pointers, but many of the concrete implementations do not need all of the parameters. For the most part these are obviously fine to just mark as UNUSED (e.g., the empty_iterator functions unsurprisingly do not do anything). Here are a few cases where I dug a little deeper (but still ended up just marking them UNUSED): - the iterator exclude_patterns is best-effort and optional (though it would be nice to support in the long run as an optimization) - ignoring the ref_store in many transaction functions is unexpected, but works because the ref_transaction itself carries enough information to do what we need. - ignoring "err" for in some cases (e.g., transaction abort) is OK because we do not return any errors. It is a little odd for reftable_be_create_reflog(), though, since we do return errors there. We should perhaps be creating string error messages at this layer, but I've punted on that for now. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-17reftable: drop obsolete test function declarationsJeff King
These functions were moved to the unit test framework in ba9661b457 (t: move reftable/record_test.c to the unit testing framework, 2024-07-02) and b34116a30c (t: move reftable/basics_test.c to the unit testing framework, 2024-05-29). The declarations in reftable-tests.h are leftover cruft. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-17reftable: ignore unused argc/argv in test functionsJeff King
There are several reftable test "main" functions that don't look at their argc/argv. They don't technically need to take these parameters, as they are called individually by cmd__reftable(). But it probably makes sense to keep them all consistent for now. In the long run these will probably all get converted to the unit-test framework anyway. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-15Merge branch 'ps/reftable-stack-compaction'Junio C Hamano
The code paths to compact multiple reftable files have been updated to correctly deal with multiple compaction triggering at the same time. * ps/reftable-stack-compaction: reftable/stack: handle locked tables during auto-compaction reftable/stack: fix corruption on concurrent compaction reftable/stack: use lock_file when adding table to "tables.list" reftable/stack: do not die when fsyncing lock file files reftable/stack: simplify tracking of table locks reftable/stack: update stats on failed full compaction reftable/stack: test compaction with already-locked tables reftable/stack: extract function to setup stack with N tables reftable/stack: refactor function to gather table sizes
2024-08-14Merge branch 'cp/unit-test-reftable-tree'Junio C Hamano
A test in reftable library has been rewritten using the unit test framework. * cp/unit-test-reftable-tree: t-reftable-tree: improve the test for infix_walk() t-reftable-tree: add test for non-existent key t-reftable-tree: split test_tree() into two sub-test functions t: move reftable/tree_test.c to the unit testing framework reftable: remove unnecessary curly braces in reftable/tree.c
2024-08-14Merge branch 'cp/unit-test-reftable-pq'Junio C Hamano
The tests for "pq" part of reftable library got rewritten to use the unit test framework. * cp/unit-test-reftable-pq: t-reftable-pq: add tests for merged_iter_pqueue_top() t-reftable-pq: add test for index based comparison t-reftable-pq: make merged_iter_pqueue_check() callable by reference t-reftable-pq: make merged_iter_pqueue_check() static t: move reftable/pq_test.c to the unit testing framework reftable: change the type of array indices to 'size_t' in reftable/pq.c reftable: remove unnecessary curly braces in reftable/pq.c
2024-08-13t: move reftable/readwrite_test.c to the unit testing frameworkChandra Pratap
reftable/readwrite_test.c exercises the functions defined in reftable/reader.{c,h} and reftable/writer.{c,h}. Migrate reftable/readwrite_test.c to the unit testing framework. Migration involves refactoring the tests to use the unit testing framework instead of reftable's test framework and renaming the tests to align with unit-tests' naming conventions. Since some tests in reftable/readwrite_test.c use the functions set_test_hash(), noop_flush() and strbuf_add_void() defined in reftable/test_framework.{c,h} but these files are not #included in the ported unit test, copy these functions in the new test file. While at it, ensure structs are 0-initialized with '= { 0 }' instead of '= { NULL }'. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Chandra Pratap <chandrapratap3519@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-08reftable/stack: handle locked tables during auto-compactionPatrick Steinhardt
When compacting tables, it may happen that we want to compact a set of tables which are already locked by a concurrent process that compacts them. In the case where we wanted to perform a full compaction of all tables it is sensible to bail out in this case, as we cannot fulfill the requested action. But when performing auto-compaction it isn't necessarily in our best interest of us to abort the whole operation. For example, due to the geometric compacting schema that we use, it may be that process A takes a lot of time to compact the bulk of all tables whereas process B appends a bunch of new tables to the stack. B would in this case also notice that it has to compact the tables that process A is compacting already and thus also try to compact the same range, probably including the new tables it has appended. But because those tables are locked already, it will fail and thus abort the complete auto-compaction. The consequence is that the stack will grow longer and longer while A isn't yet done with compaction, which will lead to a growing performance impact. Instead of aborting auto-compaction altogether, let's gracefully handle this situation by instead compacting tables which aren't locked. To do so, instead of locking from the beginning of the slice-to-be-compacted, we start locking tables from the end of the slice. Once we hit the first table that is locked already, we abort. If we succeeded to lock two or more tables, then we simply reduce the slice of tables that we're about to compact to those which we managed to lock. This ensures that we can at least make some progress for compaction in said scenario. It also helps in other scenarios, like for example when a process died and left a stale lockfile behind. In such a case we can at least ensure some compaction on a best-effort basis. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-08reftable/stack: fix corruption on concurrent compactionPatrick Steinhardt
The locking employed by compaction uses the following schema: 1. Lock "tables.list" and verify that it matches the version we have loaded in core. 2. Lock each of the tables in the user-supplied range of tables that we are supposed to compact. These locks prohibit any concurrent process to compact those tables while we are doing that. 3. Unlock "tables.list". This enables concurrent processes to add new tables to the stack, but also allows them to compact tables outside of the range of tables that we have locked. 4. Perform the compaction. 5. Lock "tables.list" again. 6. Move the compacted table into place. 7. Write the new order of tables, including the compacted table, into the lockfile. 8. Commit the lockfile into place. Letting concurrent processes modify the "tables.list" file while we are doing the compaction is very much part of the design and thus expected. After all, it may take some time to compact tables in the case where we are compacting a lot of very large tables. But there is a bug in the code. Suppose we have two processes which are compacting two slices of the table. Given that we lock each of the tables before compacting them, we know that the slices must be disjunct from each other. But regardless of that, compaction performed by one process will always impact what the other process needs to write to the "tables.list" file. Right now, we do not check whether the "tables.list" has been changed after we have locked it for the second time in (5). This has the consequence that we will always commit the old, cached in-core tables to disk without paying to respect what the other process has written. This scenario would then lead to data loss and corruption. This can even happen in the simpler case of one compacting process and one writing process. The newly-appended table by the writing process would get discarded by the compacting process because it never sees the new table. Fix this bug by re-checking whether our stack is still up to date after locking for the second time. If it isn't, then we adjust the indices of tables to replace in the updated stack. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-08reftable/stack: use lock_file when adding table to "tables.list"Patrick Steinhardt
When modifying "tables.list", we need to lock the list before updating it to ensure that no concurrent writers modify the list at the same point in time. While we do this via the `lock_file` subsystem when compacting the stack, we manually handle the lock when adding a new table to it. While not wrong, it is at least inconsistent. Refactor the code to consistently lock "tables.list" via the `lock_file` subsytem. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-08reftable/stack: do not die when fsyncing lock file filesPatrick Steinhardt
We use `fsync_component_or_die()` when committing an addition to the "tables.list" lock file, which unsurprisingly dies in case the fsync fails. Given that this is part of the reftable library, we should never die and instead let callers handle the error. Adapt accordingly and use `fsync_component()` instead. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-08reftable/stack: simplify tracking of table locksPatrick Steinhardt
When compacting tables, we store the locks of all tables we are about to compact in the `table_locks` array. As we currently only ever compact all tables in the user-provided range or none, we simply track those locks via the indices of the respective tables in the merged stack. This is about to change though, as we will introduce a mode where auto compaction gracefully handles the case of already-locked files. In this case, it may happen that we only compact a subset of the user-supplied range of tables. In this case, the indices will not necessarily match the lock indices anymore. Refactor the code such that we track the number of locks via a separate variable. The resulting code is expected to perform the same, but will make it easier to perform the described change. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-08reftable/stack: update stats on failed full compactionPatrick Steinhardt
When auto-compaction fails due to a locking error, we update the statistics to indicate this failure. We're not doing the same when performing a full compaction. Fix this inconsistency by using `stack_compact_range_stats()`, which handles the stat update for us. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-08reftable/stack: test compaction with already-locked tablesPatrick Steinhardt
We're lacking test coverage for compacting tables when some of the tables that we are about to compact are locked. Add two tests that exercise this, one for auto-compaction and one for full compaction. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-08reftable/stack: extract function to setup stack with N tablesPatrick Steinhardt
We're about to add two tests, and both of them will want to initialize the reftable stack with a set of N tables. Introduce a new function that handles this and refactor existing tests that use such a setup to use it. Note that this changes the exact records contained in the preexisting tests. This is fine though as we only care about the shape of the stack here, not the shape of each table. Furthermore, with this change we now start to disable auto compaction when writing the tables, as otherwise we might not end up with the expected amount of new tables added. This also slightly changes the behaviour of these tests, but the properties we care for remain intact. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-08reftable/stack: refactor function to gather table sizesPatrick Steinhardt
Refactor the function that gathers table sizes to be more idiomatic. For one, use `REFTABLE_CALLOC_ARRAY()` instead of `reftable_calloc()`. Second, avoid using an integer to iterate through the tables in the reftable stack given that `stack_len` itself is using a `size_t`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-04t: move reftable/tree_test.c to the unit testing frameworkChandra Pratap
reftable/tree_test.c exercises the functions defined in reftable/tree.{c, h}. Migrate reftable/tree_test.c to the unit testing framework. Migration involves refactoring the tests to use the unit testing framework instead of reftable's test framework and renaming the tests to align with unit-tests' standards. Also add a comment to help understand the test routine. Note that this commit mostly moves the test from reftable/ to t/unit-tests/ and most of the refactoring is performed by the trailing commits. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Chandra Pratap <chandrapratap3519@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-04reftable: remove unnecessary curly braces in reftable/tree.cChandra Pratap
According to Documentation/CodingGuidelines, single-line control-flow statements must omit curly braces (except for some special cases). Make reftable/tree.c adhere to this guideline. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Chandra Pratap <chandrapratap3519@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-01t-reftable-pq: make merged_iter_pqueue_check() staticChandra Pratap
merged_iter_pqueue_check() is a function previously defined in reftable/pq_test.c (now t/unit-tests/t-reftable-pq.c) and used in the testing of a priority queue as defined by reftable/pq.{c, h}. As such, this function is only called by reftable/pq_test.c and it makes little sense to expose it to non-testing code via reftable/pq.h. Hence, make this function static and remove its prototype from reftable/pq.h. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Chandra Pratap <chandrapratap3519@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>