load all archives' metadata with one Store.gather, reuse it for Archive - #10436
Merged
ThomasWaldmann merged 2 commits intoSep 26, 2026
Merged
Conversation
Listing the archives (repo-list and every archive match) read each archive metadata object with its own repository.get, i.e. one store roundtrip per archive. Archive metadata objects are small and usually each in a tiny pack of its own, which is the case Store.gather is made for. Repository.gather_many reads objects in batches (at most 1000 objects, or until 16 MiB) with one store.gather per batch, keeping the order of the ids. Objects in cached packs are sliced from the cache; unknown or still buffered ids and batches hitting a missing or truncated pack fall back to get(). Archives._infos uses it: repo-list --short for 1000 archives with 50 ms emulated store latency takes 2.0 s instead of 56 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #10436 +/- ##
==========================================
+ Coverage 88.54% 88.63% +0.09%
==========================================
Files 103 103
Lines 19148 19206 +58
Branches 2978 2990 +12
==========================================
+ Hits 16955 17024 +69
+ Misses 1529 1516 -13
- Partials 664 666 +2 ☔ View full report in Codecov by Harness. |
ThomasWaldmann
force-pushed
the
gather-archive-meta
branch
from
September 26, 2026 20:15
c143b46 to
e3fb14f
Compare
ArchiveInfo now carries the parsed ArchiveItem (as an attribute, not a tuple field, so equality and hashing are unchanged). Archive accepts an ArchiveInfo instead of a name or id; if it carries the metadata, the archive is neither looked up again (1 list + 1 load) nor its metadata loaded again (1 load). Without it, Archive looks the archive up by id as before, e.g. for the archives of borg 1.x repositories. ArchiveFormatter (repo-list, prune --list, ...), the per-archive loops of info, list, tag, find, compact, recreate and analyze, and transfer (for the source archives) pass the ArchiveInfo. repo-list with the default format for 1000 archives with 50 ms emulated store latency takes 2.1 s instead of 238 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ThomasWaldmann
force-pushed
the
gather-archive-meta
branch
from
September 26, 2026 20:24
3f63d85 to
925c55f
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.
Listing the archives read every archive's metadata object with its own
repository.get, i.e. one store roundtrip per archive. Thenrepo-list(default format) and the per-archive loops ofinfo,tag, etc. opened anArchiveper archive, which looked the archive up again (1 list + 1 load) and loaded its metadata again (1 load). Soborg repo-listfor 1000 archives made about 4000 store calls.What changes
Repository.gather_many(ids, raise_missing=True): yields the objects in the requested order, reading them in batches with oneStore.gathercall per batch (at most 1000 objects, or until 16 MiB). Archive metadata objects are small and usually each in a tiny pack of its own, which is whatStore.gather(borgstore 0.7.0) is made for: REST (ssh://) reads all ranges with one roundtrip.get(), so error handling stays as it was.get_many()(whole-pack reads) is unchanged.Archives._infos()reads the metadata objects withgather_many.ArchiveInfo.metadata: the parsedArchiveItemcomes along with each listedArchiveInfo. It is an attribute of a namedtuple subclass, not a tuple field, so equality and hashing are unchanged (prune uses sets ofArchiveInfos). Infos made without it (_make(),_replace(), legacy repos, placeholders for missing/corrupt metadata objects) havemetadata=None.Archive(manifest, archive_info):Archiveaccepts anArchiveInfo. If it carries the metadata, the archive is neither looked up nor its metadata loaded again; otherwise it is looked up by id as before.ArchiveFormatter(repo-list, prune --list, ...), the per-archive loops ofinfo,list,tag,find,compact,recreateandanalyze, andtransfer(for the source archives) pass theArchiveInfo.mountstill opens archives by id, as it opens them long after listing them.Benchmark
borg repo-listfor 1000 archives (one tiny metadata pack each), local posixfs repo,BORGSTORE_LATENCYemulating latency per store backend call (a gather is one call); median of 5 runs (0 ms) / 3 runs (50 ms):repo-listrepo-listrepo-list --shortrepo-list --shortThe output of
repo-list,repo-list --jsonandinfois identical to master's.Commits
load all archives' metadata with Store.gatherreuse the listed archive metadata when opening an ArchiveTesting
test_migrate_lock_aliveissue.gather_many: objects from several packs with one gather and no load (local,ssh:/// REST, store cache), batch limits, missing ids, missing packs, cached packs, packs still being stored._infos()gathers all metadata and keeps the order, placeholders for missing/corrupt metadata objects.ArchiveInfo.metadatatakes no part in equality/hashing;list(),get(),get_by_id()attach it.repo-list: one gather for all archives' metadata, and the default format needs no further store access;Archivewithout metadata still loads it.Not changed here: an archive whose metadata object is missing still makes
repo-listandcompactabort, see #10435.🤖 Generated with Claude Code