Skip to content

perf: speed up prune pack listing and used-blob IO - #565

Open
BradKollmyer wants to merge 7 commits into
rustic-rs:mainfrom
BradKollmyer:perf/prune-io
Open

BradKollmyer wants to merge 7 commits into
rustic-rs:mainfrom
BradKollmyer:perf/prune-io

Conversation

@BradKollmyer

@BradKollmyer BradKollmyer commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Part of #564.

First PR in the prune warm-cache stack. Speeds up pack listing and the used-blob walk’s IO shape before the CPU-side decode work.

  • Request B2’s maximum list page size.
  • Fetch 4 MiB unused pack gaps in one read.
  • Range-GET prune repack chunks in parallel.
  • Prefetch index files with extra IO workers.
  • Scale tree loaders during the used-blob walk.
  • Store prune used blob ids in a HashMap.
  • Collect index entries in bounded chunks so Vec growth cannot request a multi-hundred-MiB allocation (openzwave backup OOM while reading the index).

Unique commits vs main: main...BradKollmyer:perf/prune-io

OpenDAL only sends maxFileCount when ListOptions.limit is set. Without
it, B2 defaults to 100 names per page, so prune/check pack listing
becomes thousands of round-trips. Request 10000 for scheme b2.
256 KiB MAX_HOLESIZE split prune/restore pack reads into extra HTTP
range GETs on high-latency stores. 4 MiB is cheaper than another RTT
on a ~100 Mbps link.
Unused gaps in a pack were fetched one after another. Issue those
range GETs in parallel; the packer already serializes writes.
stream_list used CPU-count Rayon workers and a zero-capacity channel,
so reading index/snapshots was one GET at a time on high-latency
backends. Use 16-32 workers with prefetch so GETs overlap decrypt/parse.
TreeStreamerOnce was hardcoded to 4 workers, which left B2 tree-pack
GETs idle. Use 2x CPUs (8-32) with a larger out channel, and a HashSet
for visited tree ids.
The used-id set was a BTreeMap, so inserts got slower as prune walked
more blobs. A HashMap keeps that path O(1).
A single Vec of blob ids doubles on growth. On a large repo that request
is hundreds of MiB while the old buffer is still live, and musl aborts
(openzwave: memory allocation of 807665664 bytes failed at reading
index 906/1551). Store ids and full entries in 1 Mi-entry chunks and
binary-search each sorted chunk.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant