You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
perf: Skip request-body compression for small payloads (#988)
Until now the Python client compressed every request body regardless of
size. It now skips compression below 1024 B.
Closes#934
## Why 1024 B
- **Parity with the JS client.** It uses the same threshold, the same
operator (`>=`), and the same byte-based measurement
(`MIN_COMPRESS_BYTES` in `apify-client-js/src/utils.ts`), so both
clients now put identical bytes on the wire.
- **Small bodies grow.** `run.charge` 40 B to 59 B, `kvs.set_record` 27
B to 45 B, single-item `dataset.push_items` 10 B to 30 B. gzip breaks
even only at ~88 B of realistic JSON, and high-entropy bodies such as
binary key-value store records inflate at any size, by ~23 B for gzip
and ~4 B for brotli.
- **No packet is saved below ~1 KB**, and removing a packet is the only
thing that buys latency. With ~500 B of request headers on a 1400 B MSS:
| Raw body | gzipped | Segments raw to gzip | Packet saved? |
| --- | --- | --- | --- |
| 786 B | 344 B | 1 to 1 | no |
| 1071 B | 447 B | 2 to 1 | yes |
| 8250 B | 2958 B | 7 to 3 | yes |
Compressing everything instead, the reverse direction raised in the
issue, doesn't pay off. Over 1200 realistic bodies it inflates 749 of
them while total bytes move only from −62.1% to −63.8%, and that gain
sits entirely in the 512–1024 B band where no packet is saved anyway. At
1024 B nothing inflates.
## Changes
- `MIN_COMPRESSION_SIZE = 1024`. `_prepare_request_call` compresses only
at or above it, measured on the encoded bytes so a multibyte `str` is
judged correctly.
- A caller-supplied `Content-Encoding` is now dropped on a skipped body,
where it would otherwise survive and mislabel an uncompressed payload.
- The async client skips the `asyncio.to_thread` hop for any body it
won't compress, the hop costing 36–68 µs against 5–12 µs of compression.
`_is_body_worth_compressing` sits next to the rule it mirrors so the two
can't drift apart. A `json=` body still hops, its size being unknown
until serialized.
- Docs: new "Minimum body size" section. The page claimed the client
compresses every request body.
## Verification
Against the live API, 8 value shapes round-trip byte-identical with and
without compression, small uncompressed bodies are accepted by
`dataset.push_items`, `rq.add_request`, `rq.batch_add_requests`,
`dataset.update`, `schedules().create`, `schedule.update`, and
`webhooks().create`, and latency is unchanged.
Tests that compressed tiny bodies were passing vacuously. They now cover
the 0/1/1023/1024/1025 boundary for both gzip and brotli, the
byte-vs-character threshold, the dropped header, and the thread-hop
decisions. `test_run_charge`'s `compression` axis was the suite's only
end-to-end compression coverage and had gone vacuous on its 39-byte
body, so it's replaced by a test asserting an above-threshold body
reaches the server compressed under the configured algorithm.
*✍️ Drafted by Claude Code*
Copy file name to clipboardExpand all lines: docs/02_concepts/13_http_compression.mdx
+6-2Lines changed: 6 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,7 +15,11 @@ The Apify client compresses request bodies before sending them to the API. It re
15
15
16
16
## How it works
17
17
18
-
The client compresses request bodies using the compressor configured via the `compression` parameter (default `'gzip'`). The server supports both gzip and brotli and decompresses the request body transparently.
18
+
The client compresses request bodies using the compressor configured via the `compression` parameter (default `'gzip'`). The server supports both gzip and brotli and decompresses the request body transparently. A body is compressed only when it is large enough to benefit and its content type isn't already compressed, as the next two sections describe.
19
+
20
+
## Minimum body size
21
+
22
+
The client sends bodies smaller than 1024 bytes without compression and without the `Content-Encoding` header. A body of this size fits in one network packet, so compression doesn't remove a network round trip and only costs CPU time. For very small bodies, the compression format adds bytes and can make the body larger.
You can also implement a fully custom compressor by subclassing `HttpCompressor`:
91
+
You can also implement a fully custom compressor by subclassing `HttpCompressor`. The client calls it only for bodies that reach the [minimum body size](#minimum-body-size) and aren't [already compressed](#already-compressed-payloads):
0 commit comments