docs: fix chunks index / memory usage internals#9941
Merged
ThomasWaldmann merged 1 commit intoJul 23, 2026
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #9941 +/- ##
=======================================
Coverage 85.49% 85.49%
=======================================
Files 93 93
Lines 16122 16122
Branches 2466 2466
=======================================
Hits 13783 13783
Misses 1632 1632
Partials 707 707 ☔ View full report in Codecov by Harness. |
The internals docs still described the borg 1.x C HashIndex and a chunks index value of (refcount, size). Both are gone since the switch to borghash and the pack-based repository layout. - chunks index: document the real ChunkIndexEntry (flags, size, pack_id, obj_offset, obj_size), 32 + 48 == 80 bytes per entry, and that this is also the exact on-disk size per entry. Also document that flags and size are zeroed when an index fragment is written, so a freshly built chunks index has size == 0 everywhere and the plaintext size is only filled in for the chunks processed by the current borg run. - files cache: document that entries are "compressed" (chunk ids reduced to 32bit chunks index indexes) and msgpacked, instead of claiming they are python objects with a lot of overhead. - memory usage: correct the formulas. Measured with 1Mi entries on CPython 3.11 / 64bit: chunks index ~100 B/entry (80 B payload + kv over-allocation of up to 30% + 4 B per bucket at load factor 0.25 .. 0.5, so 88 .. 120 B), files cache ~230 B per file + ~6 B per chunk. The old numbers (40 / 240 + 165) and the mem_allocation / load_factor formulas predated borghash; the latter also double-counted the hash table overhead, which is now included in the per-entry numbers. Updated the two worked examples accordingly. - HashIndex: describe borghash's actual layout (bucket table of uint32 indexes + separate keys/values arrays), its real grow/shrink thresholds (grow 2x above load factor 0.5 incl. tombstones, shrink to 40% below 0.10, min 1000 buckets) and the BORGHASH serialization format, instead of the removed BORG_IDX / struct HashHeader / MAX_VALUE in-band signalling description. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ThomasWaldmann
force-pushed
the
docs-chunks-index-memory
branch
from
July 23, 2026 09:45
ed84821 to
e5ed8f6
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.
The internals docs still described the borg 1.x C
HashIndexand a chunks index value of(refcount, size). Both are gone since the switch to borghash and the pack-based repository layout, so the "chunks index" / "memory usage" / "HashIndex" sections were misleading and the memory formulas were off by a lot.chunks index
Document the real
ChunkIndexEntry(flags,size,pack_id,obj_offset,obj_size), 32 + 48 == 80 bytes per entry, which is also the exact on-disk size per entry.Also document that
_store_chunkindex_fragmentzeroes flags and size before writing a fragment, so a freshly built chunks index hassize == 0for all entries (both from the fragments and from the slow pack-header rebuild). The plaintext size is only filled in for the chunks the current borg run actually processes.files cache
Document that entries are "compressed" (chunk ids reduced to 32bit chunks index indexes via
k_to_idx) and msgpacked, instead of claiming they are python objects with a lot of overhead. Also note that the on-disk format stores the uncompressed chunks list, since the indexes are only valid for one specific in-memory chunks index.memory usage
chunks_index_usagechunk_count * 40chunk_count * 100files_cache_usagetotal_file_count * 240 + chunk_count * 165total_file_count * 230 + chunk_count * 6Measured rather than derived: filling a real
ChunkIndex/ files cache dict with 1Mi entries and reading RSS, on CPython 3.11 / 64bit.230 + 6 * chunksfits that within ~5%. The per-chunk number dropped from 165 to 6 because the files cache now stores 32bit indexes instead of 256bit chunk ids.Also dropped the
mem_allocation = mem_usage / load_factor/mem_allocation_peakformulas: they predate borghash and now double-count, as the hash table overhead is already included in the per-entry numbers. Replaced by a short note on the resize peaks. Both worked examples were recomputed (1.9GiB / 0.28GiB, was 2.8GiB / 0.31GiB).HashIndex
Rewritten for borghash: the three-array layout (uint32 bucket table + separate keys/values arrays), the stable keys-array index the files cache relies on, the real thresholds (grow 2x above load factor 0.5 including tombstones, shrink to 40% below 0.10, floor of 1000 buckets, kv arrays grown by 1.3x), and the
BORGHASH+ JSON metadata serialization format.Removed the description of the removed C implementation:
BORG_IDX/struct HashHeader, the 75%/25% thresholds, the 93% effective load factor, and theMAX_VALUEin-band signalling (values are no longer stored in the buckets, so there is no in-band signalling in the value anymore).The
internals_hashindexlabel is kept -repo_create_cmd.pyreferences it for thenoneencryption DoS note, which still applies (linear probing on attacker-chosen key prefixes).make htmlbuilds clean (the single warning,misc/asciinema/README.rstnot in a toctree, is pre-existing and unrelated).🤖 Generated with Claude Code