Repository navigation
Conversation
handleAll copied every remaining destination range whole once the import source ran out. With an empty base nothing stopped it, so ranges that straddle or sit inside the import prefix kept their old objects. Apply the validWritingRange guard the merge main loop already uses, so such ranges are entered and filtered by SkipPrefixIterator. Plain merges are unaffected. Closes treeverse#10572
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #10572
Change Description
Background
Import is a merge with an empty base, where the destination iterator is wrapped in
SkipPrefixIteratorso that everything under the import prefixes is dropped ("prefixes completely overridden",graveler.go). The merge main loop only copies a whole destination range whenvalidWritingRange(m.dest)says the range does not touch an import prefix.handleAll, which drains whichever side is left after the other runs out, had no such check.Bug Fix
p/instead of 1. The leaked ranges keep their original range IDs, so nothing reports them as changed.handleAll(dest)copies each remaining destination range withWriteRange. With an empty base,getNextOverlappingFromBaseis always nil, so every remaining range is copied whole, including ranges that straddle the prefix or lie entirely inside it.SkipPrefixIterator.NextRange()skips ranges entirely inside the prefix, butSkipPrefixIterator.Next()can stop on such a range's head after skipping records across a range boundary.handleAllthen copies that range too. That is the path the issue's repro takes.handleAll, only copy a range whole whenvalidWritingRange(iter)is true, the same guard the two main-loop copy sites already use. Otherwise enter the range soSkipPrefixIteratorfilters its records. For plain mergesvalidWritingRangeis always true (their iterators are notImportIterators), so merge behavior is unchanged.Testing Details
New
Test_importcases (pkg/graveler/committed/import_test.go). Each lists the exactWriteRange(range copied whole) andWriteRecord(record written after filtering) calls expected:handleAll; ranges outside the prefix are still copied whole (fails on master)MinKeyandMaxKey, plus keys equal to the prefix string (fails on master)""prefix replaces the whole branch (guard, passes before and after)Commands:
Why synthetic unit cases: the bug depends on where range boundaries fall relative to the prefix. Range breaks are
FNV-64a(key) % range_raggedness_entries == 0, so a natural repro depends on the hashes of specific key names and on the default settings. The unit cases spell out each range layout directly, so they do not depend on hashes or settings.Manual check with a local
lakefs run --quickstart(local blockstore import), unfixed master vs this branch:p/; this branch leaves 1 (p/f).committed.permanent.range_raggedness_entries: 20, delete all but 10 source files, re-import into the samep/: master leaves 19 objects (p/f0029..p/f0037leaked); this branch leaves 10.Objects outside the prefix (
a,q) survive in every run. An esti test is possible because the issue's 2000-file layout splits deterministically, but it would depend on default settings and key-name hashes. I can add one if you want it.Breaking Change?
No. Plain merges are unchanged. Import now removes old objects under the prefix, as the docs already say it does. One cost difference: when the source runs out before a large old prefix, those ranges are now read record by record instead of being copied wrongly.
Additional info
strings.HasPrefix), and object-type import paths are passed as prefixes too. Importing an object tox/file1therefore also removesx/file10from the destination. This comes from reading the code; I did not run it.SkipPrefixIterator.Next()does not checkIsCurrentRangeBoundedByPrefixon a range head it reaches while skipping records (NextRange()does). With this fix that no longer affects correctness, since every copy site checksvalidWritingRange. But those ranges are scanned record by record instead of skipped, which already happens in the merge main loop today.Contact Details
Via this PR.
Drafted-by: Claude Opus 5.5; reviewed by @Vamsi-klu before posting