Skip to content

Commit a22c52b

Browse files
docs: Version docs for v3.1.2 [skip ci]
1 parent 799a909 commit a22c52b

6 files changed

Lines changed: 15148 additions & 14423 deletions

File tree

website/versioned_docs/version-3.1/02_concepts/13_http_compression.mdx

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,74 @@
11
---
22
id: http-compression
33
title: HTTP compression
4-
description: The client compresses every request body automatically using gzip by default, with optional brotli via an explicit opt-in.
4+
description: The client compresses request bodies automatically using gzip by default, with optional brotli via an explicit opt-in.
55
---
66

7-
The Apify client compresses every request body before sending it to the API. It reduces the amount of data transferred over the network, resulting in faster requests and lower bandwidth usage, especially for large payloads such as Actor inputs, dataset uploads, or key-value store records.
7+
import Tabs from '@theme/Tabs';
8+
import TabItem from '@theme/TabItem';
9+
import CodeBlock from '@theme/CodeBlock';
10+
11+
import SkipCompressionAsyncExample from '!!raw-loader!./code/13_skip_compression_async.py';
12+
import SkipCompressionSyncExample from '!!raw-loader!./code/13_skip_compression_sync.py';
13+
import PrecompressedAsyncExample from '!!raw-loader!./code/13_precompressed_async.py';
14+
import PrecompressedSyncExample from '!!raw-loader!./code/13_precompressed_sync.py';
15+
16+
The Apify client compresses request bodies before sending them to the API. It reduces the amount of data transferred over the network, resulting in faster requests and lower bandwidth usage, especially for large payloads such as Actor inputs, dataset uploads, or key-value store records.
817

918
## How it works
1019

11-
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.
20+
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's large enough to benefit, its content type isn't already compressed, and the request carries no `Content-Encoding` of its own. For details, see [Minimum body size](#minimum-body-size), [Already-compressed payloads](#already-compressed-payloads), and [Pre-compressed bodies](#pre-compressed-bodies).
21+
22+
## Minimum body size
23+
24+
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.
25+
26+
## Already-compressed payloads
27+
28+
Some payloads carry their own compression, so compressing them again costs CPU and memory while making the request slightly larger. The client skips compression when the request's `Content-Type` is one of these:
29+
30+
- any `image/*`, `audio/*`, or `video/*` type
31+
- archives such as `application/zip`, `application/gzip`, or `application/x-7z-compressed`
32+
- office documents and packages built on ZIP, such as `.docx`, `.xlsx`, `.epub`, or `.apk`
33+
- web fonts (`font/woff`, `font/woff2`)
34+
35+
Two kinds of media type are compressed anyway: raw formats such as `image/bmp`, `image/tiff`, and `audio/wav`, and subtypes with a structured syntax suffix such as `image/svg+xml`. Set an accurate `content_type` when uploading media to a key-value store:
36+
37+
<Tabs>
38+
<TabItem value="AsyncExample" label="Async client" default>
39+
<CodeBlock className="language-python">
40+
{SkipCompressionAsyncExample}
41+
</CodeBlock>
42+
</TabItem>
43+
<TabItem value="SyncExample" label="Sync client">
44+
<CodeBlock className="language-python">
45+
{SkipCompressionSyncExample}
46+
</CodeBlock>
47+
</TabItem>
48+
</Tabs>
49+
50+
Without an explicit content type, a `bytes` value is sent as `application/octet-stream`, which the client can't tell apart from uncompressed binary data and therefore still compresses. File-like values are read into memory before they're sent, so they follow the same rules as any other body.
51+
52+
## Pre-compressed bodies
53+
54+
A payload can reach the client already encoded, for example a gzipped file read from disk. Set the `Content-Encoding` header to name the encoding the payload carries. The client then sends the body as it is and forwards the header, so nothing gets compressed twice. `set_record` exposes the header as its `content_encoding` argument:
55+
56+
<Tabs>
57+
<TabItem value="AsyncExample" label="Async client" default>
58+
<CodeBlock className="language-python">
59+
{PrecompressedAsyncExample}
60+
</CodeBlock>
61+
</TabItem>
62+
<TabItem value="SyncExample" label="Sync client">
63+
<CodeBlock className="language-python">
64+
{PrecompressedSyncExample}
65+
</CodeBlock>
66+
</TabItem>
67+
</Tabs>
68+
69+
The header is forwarded verbatim, so it also covers encodings the client ships no compressor for, such as `deflate`. The API accepts `gzip`, `br`, `deflate`, and `identity`. Passing `identity` turns compression off for a single request without changing how the client is configured.
70+
71+
A value that can't be compressed at all - a string, an object serialized to JSON, or a file-like value opened in text mode - is rejected with a `TypeError` when `content_encoding` names a compression. Beyond that the client can't verify that the bytes match the header, so set `Content-Encoding` only when the payload really is encoded that way. Key-value store records are stored exactly as you upload them, which makes the header part of the stored record rather than a transport detail.
1272

1373
## Configuration
1474

@@ -51,7 +111,7 @@ client = ApifyClient(token='MY-APIFY-TOKEN', compression=BrotliHttpCompressor(qu
51111
client = ApifyClient(token='MY-APIFY-TOKEN', compression=GzipHttpCompressor(quality=9))
52112
```
53113

54-
You can also implement a fully custom compressor by subclassing `HttpCompressor`:
114+
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) or [pre-compressed by the caller](#pre-compressed-bodies):
55115

56116
```python
57117
from apify_client import ApifyClient
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import asyncio
2+
import gzip
3+
from pathlib import Path
4+
5+
from apify_client import ApifyClientAsync
6+
7+
TOKEN = 'MY-APIFY-TOKEN'
8+
9+
10+
async def main() -> None:
11+
apify_client = ApifyClientAsync(TOKEN)
12+
kvs_client = apify_client.key_value_store('MY-KVS-ID')
13+
14+
report = await asyncio.to_thread(Path('report.csv').read_bytes)
15+
compressed_report = await asyncio.to_thread(gzip.compress, report)
16+
17+
# The explicit content encoding stops the client from compressing the bytes again.
18+
await kvs_client.set_record(
19+
'report',
20+
compressed_report,
21+
content_type='text/csv',
22+
content_encoding='gzip',
23+
)
24+
25+
26+
if __name__ == '__main__':
27+
asyncio.run(main())
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import gzip
2+
from pathlib import Path
3+
4+
from apify_client import ApifyClient
5+
6+
TOKEN = 'MY-APIFY-TOKEN'
7+
8+
9+
def main() -> None:
10+
apify_client = ApifyClient(TOKEN)
11+
kvs_client = apify_client.key_value_store('MY-KVS-ID')
12+
13+
report = Path('report.csv').read_bytes()
14+
compressed_report = gzip.compress(report)
15+
16+
# The explicit content encoding stops the client from compressing the bytes again.
17+
kvs_client.set_record(
18+
'report',
19+
compressed_report,
20+
content_type='text/csv',
21+
content_encoding='gzip',
22+
)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import asyncio
2+
from pathlib import Path
3+
4+
from apify_client import ApifyClientAsync
5+
6+
TOKEN = 'MY-APIFY-TOKEN'
7+
8+
9+
async def main() -> None:
10+
apify_client = ApifyClientAsync(TOKEN)
11+
kvs_client = apify_client.key_value_store('MY-KVS-ID')
12+
13+
screenshot = await asyncio.to_thread(Path('screenshot.png').read_bytes)
14+
15+
# The explicit content type lets the client skip compressing the PNG.
16+
await kvs_client.set_record('screenshot', screenshot, content_type='image/png')
17+
18+
19+
if __name__ == '__main__':
20+
asyncio.run(main())
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from pathlib import Path
2+
3+
from apify_client import ApifyClient
4+
5+
TOKEN = 'MY-APIFY-TOKEN'
6+
7+
8+
def main() -> None:
9+
apify_client = ApifyClient(TOKEN)
10+
kvs_client = apify_client.key_value_store('MY-KVS-ID')
11+
12+
screenshot = Path('screenshot.png').read_bytes()
13+
14+
# The explicit content type lets the client skip compressing the PNG.
15+
kvs_client.set_record('screenshot', screenshot, content_type='image/png')

0 commit comments

Comments
 (0)