Skip to content

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

Closed

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

Activity

  1. added
    enhancementNew feature or request.
    t-toolingIssues with this label are in the ownership of the tooling team.
    on Jul 21, 2026
  2. vdusek commented on Jul 21, 2026

    @vdusek
    ContributorAuthor

    Basically, we should:

    • Skip the compression for the streams.
    • Provide the stream to the underlying HTTP client.

    If I am not mistaken, this is done in the apify-client-js using axios as the HTTP client.

    @barjin Does impit support streaming request bodies? According to Claude, it doesn't seem to, but I would prefer an answer from you 😄.

  3. barjin commented on Jul 21, 2026

    @barjin
    Member

    That's right, impit currently only streams responses in Python. Sounds like a useful addition, though 👍 I added an issue here.

  4. self-assigned this
    on Sep 29, 2026
  5. added a commit that references this issue on Sep 30, 2026
    9be215b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions