Skip to content

Support streaming request bodies to avoid buffering large uploads in memory #972

Description

@vdusek

File-like values passed to KeyValueStoreClient.set_record (and Actor run inputs) are fully read into memory by value.read() in encode_key_value_store_record_value, and _prepare_request_call then compresses the whole buffer into a second full copy. Uploading a large record (e.g. multi-GB) from a file handle needs ~2x its size resident in RAM.

Root cause

There is no chunked/streaming path anywhere in the stack:

  • HttpClient.call / HttpClientAsync.call type the body as data: str | bytes | bytearray | None — no iterator or file object.
  • HttpClientBase._prepare_request_call reads the whole data and compresses it in one shot: self._http_compressor.compress(data) runs over the full buffer, regardless of the configured HttpCompressor (applies to both gzip and brotli).
  • The default transport (ImpitHttpClient) only accepts a bytes-like content= body.

Notes on a fix

Full streaming requires changes across the whole layer, not just the transport:

  1. Accept a streaming body (bytes iterator / async iterator / file object) through the HttpClient abstraction.
  2. Make compression incremental or skippable, so the body is not re-buffered. This has to hold for every HttpCompressor implementation (gzip and brotli), not a single one.
  3. Use a transport that can stream the body. httpx supports this today — content= accepts a sync/async bytes generator and streams it chunk-by-chunk (multipart uploads via files= stream by default). It is unclear whether impit exposes an equivalent; that needs checking, and impit was chosen for its browser-like TLS fingerprints, so swapping transports has trade-offs.

Follow-up to commit bc092b8 (file-like read fix), where this was noted as out of scope.

✍️ Drafted by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request.t-toolingIssues with this label are in the ownership of the tooling team.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions