Skip to content

release chunk data memoryviews after use, see #1755 - #9978

Merged
ThomasWaldmann merged 2 commits into
borgbackup:masterfrom
ThomasWaldmann:release-chunk-data
Jul 30, 2026
Merged

release chunk data memoryviews after use, see #1755#9978
ThomasWaldmann merged 2 commits into
borgbackup:masterfrom
ThomasWaldmann:release-chunk-data

Conversation

@ThomasWaldmann

Copy link
Copy Markdown
Member

Summary

Chunkers wrap every yielded chunk in a memoryview, but no consumer ever released it.

On pypy this is fatal: a memoryview over a C-API-created bytes object pins the buffer via cpyext, and if it is dropped without .release(), the memory is never reclaimed — not even by gc.collect(). borg create leaked ~2-3x the processed data volume (a 20 GB create reached a 43 GB footprint and was killed by the OS). On CPython, explicit release simply frees the buffer timely instead of relying on refcounting of the last reference.

Changes:

  • new release_chunk_data() helper in chunkers/reader.pyx; consumer obligation documented in the Chunk docstring.
  • call it at all places consuming chunker output: ChunkBuffer.flush, ChunksProcessor.process_file_chunks, ArchiveRecreater.chunk_processor, transfer --rechunk, chunker benchmark.
  • fix a pre-existing AttributeError in transfer --rechunk --dry-run for all-zero chunks (chunk.sizechunk.meta["size"]).

Split out of #9976 (pypy support, see #1755) since it is useful independently of pypy; verified there by footprint sampling: multi-GB creates on pypy now stay flat at ~160 MB (was: linear growth to 43 GB for 20 GB input). Full test suites green on CPython 3.11 and pypy3.11.

🤖 Generated with Claude Code

On pypy, a memoryview over a C-API-created bytes object pins the
buffer via cpyext: if the consumer drops the memoryview without
calling .release(), the memory is never reclaimed - not even by
gc.collect(). Since the chunkers wrap every yielded chunk in a
memoryview, borg create leaked roughly 2-3x the processed data
volume on pypy (a 20 GB create grew to a 43 GB memory footprint
and got killed by the OS).

Add release_chunk_data() to chunkers and call it at all places
that consume chunker output: ChunkBuffer.flush,
ChunksProcessor.process_file_chunks, ArchiveRecreater.chunk_processor,
transfer --rechunk and the chunker benchmark. Explicitly releasing
also frees the buffer timely on CPython instead of relying on
refcounting of the last reference. Document the consumer
obligation in the Chunk docstring.

Memory now stays flat for large creates on pypy (~160 MB for a
multi-GB create instead of linear growth).

Also fix a pre-existing AttributeError in transfer --rechunk
--dry-run: Chunk namedtuples have no .size attribute, all-zero
chunks need chunk.meta["size"].

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.85%. Comparing base (28de45c) to head (5ce324c).
⚠️ Report is 7 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9978      +/-   ##
==========================================
+ Coverage   85.83%   85.85%   +0.02%     
==========================================
  Files          95       95              
  Lines       17034    17067      +33     
  Branches     2607     2611       +4     
==========================================
+ Hits        14621    14653      +32     
- Misses       1673     1674       +1     
  Partials      740      740              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@ThomasWaldmann
ThomasWaldmann merged commit 63de66b into borgbackup:master Jul 30, 2026
19 checks passed
@ThomasWaldmann
ThomasWaldmann deleted the release-chunk-data branch July 30, 2026 20:17
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