]> git.99rst.org Git - git.git/commit
reftable: fix quadratic behavior in the presence of tombstones
authorKristofer Karlsson <redacted>
Fri, 10 Jul 2026 10:36:07 +0000 (10:36 +0000)
committerJunio C Hamano <redacted>
Fri, 10 Jul 2026 15:18:56 +0000 (08:18 -0700)
commit146a94632139fc470af1fc118b5ff3dda2d15b40
tree0efd8252940f32360b6c6bfbd276b0bbf15d4ab4
parent13d8160f4922d88aa26eb2eea74cab3fa29848b8
reftable: fix quadratic behavior in the presence of tombstones

When many tombstones are present in a reftable, operations that need
to look up or iterate over refs exhibit quadratic behavior.  With
8000 refs deleted and re-created, update-ref takes ~15s, quadrupling
for each doubling of input size.

The root cause is the merged iterator's suppress_deletions flag.
When set, merged_iter_next_void() silently consumes tombstone records
in a tight internal loop before returning to the caller.  This
prevents higher-level code from checking iteration bounds (such as
prefix or refname comparisons) until after all tombstones have been
scanned.

This affects any code path that seeks into a range containing
tombstones, including:

 - refs_verify_refnames_available() seeks to "refs/tags/foo-1/" to
   check for D/F conflicts and must scan through all subsequent
   tombstones before the caller can see that they are past the prefix
   of interest.

 - reftable_backend_read_ref() seeks to a specific refname and must
   scan through all subsequent tombstones before returning "not
   found", because the merged iterator skips the matching tombstone
   and searches for the next live record.

Fix this by making suppress_deletions configurable via
reftable_stack_options instead of unconditionally enabling it.  Git
no longer sets the flag, so tombstones are now returned to callers in
the reftable backend, which skip them after their existing bounds
checks.  This allows iteration to terminate as soon as a tombstone
past the relevant bound is encountered.

Downstream users of the reftable library (e.g. libgit2) can still
enable suppress_deletions through the stack options to retain the
previous behavior.

This also requires adding deletion checks to the log iteration paths,
since suppress_deletions applied to both ref and log iterators.

Both tests in p1401 go from ~13s to ~0.2s with this change.

Reported-by: Jeff King <redacted>
Signed-off-by: Kristofer Karlsson <redacted>
Signed-off-by: Junio C Hamano <redacted>
refs/reftable-backend.c
reftable/reftable-stack.h
reftable/stack.c
git clone https://git.99rst.org/PROJECT