Skip to content

fix(client): stream parallel-upload slices instead of buffering in memory - #4

Merged
injaeryou merged 1 commit into
mainfrom
fix/parallel-upload-streaming-slice
Jun 29, 2026
Merged

fix(client): stream parallel-upload slices instead of buffering in memory#4
injaeryou merged 1 commit into
mainfrom
fix/parallel-upload-streaming-slice

Conversation

@injaeryou

Copy link
Copy Markdown
Owner

Purpose

Closes #3. Parallel uploads read each byte-range slice fully into memory (io.BytesIO(f.read(length))), so with parallel_uploads=N the in-flight slices summed to the whole file — an OOM risk for large files. Raised by gemini-code-assist on #1, deferred from #2.

Changes

  • Add resumable_upload/client/_fileslice.pyFileSlice, a read-only, seekable, range-bounded (io.RawIOBase) view that reads on demand. The uploader pulls one chunk_size block at a time, so memory use is one chunk per active slice instead of the whole slice.
  • Wire it into both clients for parity: sync client/parallel.py and async client/aio/client.py (replacing the old _read_slice + BytesIO).
  • Each path closes its FileSlice explicitly — the uploader treats an injected stream as not-owned and will not close it.

TUS compliance

Client-side only. No change to wire bytes, headers, status codes, or offset semantics — same bytes are sent, just read incrementally from disk.

Test Plan

pytest -q
ruff check resumable_upload tests && ruff format --check resumable_upload tests
ty check resumable_upload

Test Result

  • pytest503 passed, 4 skipped (+5 new FileSlice unit tests; existing sync+async parallel/concat suites now exercise FileSlice end-to-end)
  • ruff check / ruff format --check / ty check — clean

🤖 Generated with Claude Code

…mory

Parallel uploads read each byte-range slice fully into memory via
`io.BytesIO(f.read(length))`, so with `parallel_uploads=N` the in-flight
slices summed to the whole file — an OOM risk for large files.

Add `FileSlice`, a read-only, seekable, range-bounded file view that reads
on demand. The uploader pulls one `chunk_size` block at a time, so memory
use is one chunk per active slice instead of the whole slice. Wired into
both the sync (`parallel.py`) and async (`aio/client.py`) paths for parity;
the uploader treats the injected stream as not-owned, so each path closes
its FileSlice explicitly.

Closes #3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@injaeryou
injaeryou merged commit 7eab64e into main Jun 29, 2026
10 checks passed
@injaeryou
injaeryou deleted the fix/parallel-upload-streaming-slice branch June 29, 2026 13:46
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.

Parallel upload loads each slice fully into memory (potential OOM)

1 participant