write_chunkindex_to_repo: reduce memory needs - #10078
Merged
ThomasWaldmann merged 1 commit intoAug 11, 2026
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #10078 +/- ##
=======================================
Coverage 86.69% 86.70%
=======================================
Files 98 98
Lines 17166 17174 +8
Branches 2607 2609 +2
=======================================
+ Hits 14882 14890 +8
Misses 1586 1586
Partials 698 698 ☔ View full report in Codecov by Harness. |
A full index rewrite used to build one sorted Python list of all keys (~90 bytes per key, e.g. ~9 GB for a 100M chunks repo). As the keys are uniformly distributed hash digests, partition them by their leading key bits into 2 ** prefix_bits similarly sized, disjoint sets and select / sort / write one partition's keys at a time, so the extra memory needed is bounded by one partition (~ one fragment, <= 32 MB worth of keys), independent of the index size. Selecting a partition is a cheap, C-level filtering scan of the hash table (borghash 0.2.0 items(prefix_bits=..., prefix=...)), so runtime stays unchanged (measured: 21s for a 5M-entry full rewrite either way, peak extra memory 480 MB -> 85 MB). Partition membership and prefix_bits (chosen from the entry count) only depend on the selected entries, so identical entry sets still produce identical fragments (writing/repacking stays idempotent and convergent), and as the prefix compares the keys' leading bits, ascending prefixes yield the same globally sorted key sequence as one all-keys sort did. ChunkIndex gets a new_count property (count of F_NEW entries, maintained incrementally) so the incremental write path can pick prefix_bits without an extra counting pass; as a bonus, a nothing-new incremental write now skips the full table scan entirely. Requires borghash ~= 0.2.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ThomasWaldmann
force-pushed
the
chunkindex-mem-9886
branch
from
August 11, 2026 15:45
7b8eba9 to
4f861a3
Compare
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.
Fixes #9886.
A full index rewrite used to build one sorted Python list of all keys (about 90 bytes per key, e.g. about 9 GB for a 100M chunks repo). As the keys are uniformly distributed hash digests, this partitions them by their leading key bits into
2 ** prefix_bitssimilarly sized, disjoint sets and selects / sorts / writes one partition's keys at a time, so the extra memory needed is bounded by one partition (about one fragment, at most 32 MB worth of keys), independent of the index size.borghash ~= 0.2.0(CI will stay red on the dependency until 0.2.0 is released).prefix_bitsis chosen from the selected-entry count, aiming about 5% belowCHUNKINDEX_FRAGMENT_ENTRIES_MAX. That headroom matters because rounding the partition count up to a power of two would otherwise be able to put the expected partition size right at MAX. With hash digests as keys the partition sizes are tightly concentrated around their expectation, so every partition lands well below MAX and becomes exactly one fragment: at 5M entries and MAX=400k, the 16 partitions measure 311,896 … 313,950 entries, i.e. >100 sigma of margin.prefix_bits = 0, so there is a single partition, no filtering scans, and behavior is exactly as before.prefix_bitsonly depend on the selected entries, so identical entry sets still produce identical fragments; and as the prefix compares the keys' leading bits, ascending prefixes yield the same globally sorted key sequence as one all-keys sort did.ChunkIndexgets anew_countproperty (count of F_NEW entries, maintained incrementally; computed lazily for tables loaded from a file), so the incremental path can pickprefix_bitswithout a counting pass. Bonus: a nothing-new incremental write now skips the full table scan entirely.Measured (5M uniformly distributed entries, full rewrite, macOS vmmap physical footprint peak): peak extra memory of the write 480 MB → 85 MB, runtime unchanged (21.2 s vs 21.4 s incl. serialization + store).
New tests: real partitioning with uniformly distributed keys (bounded fragments, disjoint + complete + contiguous ranges of the sorted key space, rebuild round-trip, incremental and full), convergence with shuffled insertion order,
new_countmaintenance (insert/overwrite/delete/clear_new/clear/failed insert/file round-trip) and prefix-filterediteritems.🤖 Generated with Claude Code