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!: serialize requests once in batchAddRequests (#1051)
### Description
- Mirroring apify/apify-client-python#953.
- `batchAddRequests` stringified every request twice: once in
`sliceArrayByByteLength` to measure the batch, and again in the
`serializeRequest` interceptor when sending. Axios then ran its default
`transformRequest` on top, because `transformRequest: undefined` in the
instance config falls back to the defaults, and that default validates a
JSON string body by parsing it in full. Each batch body was stringified
twice and parsed once more.
- Each request is now serialized once up front. The byte lengths decide
the batch boundaries (commas and brackets counted, which the old
measurement skipped) and the same strings are joined into the batch
body, sent with an explicit `content-type: application/json`. The
interceptor passes a string body with an explicit content type through
untouched, and the axios instance sets `transformRequest` and
`transformResponse` to `[]`. That also removes the validation re-parse
for every JSON body the client sends.
### Issue
- Closes#972
### Breaking changes
- A string body declared as JSON but not valid JSON (for example
`setRecord` with `contentType: 'application/json'` and a non-JSON
string) is sent as it is. Axios used to double-encode it into a JSON
string literal.
- A request too large for the payload limit is rejected before any batch
is sent. It used to be detected only when its batch came up, with
earlier batches already in flight.
- The protected `_batchAddRequests` and `_batchAddRequestsWithRetries`
take the serialized entries, and `_batchAddRequests` no longer
re-validates a batch the public method already validated. The API report
is updated.
*✍️ Drafted by Claude Code*
Copy file name to clipboardExpand all lines: docs/04_upgrading/upgrading_v3.md
+31Lines changed: 31 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -272,6 +272,37 @@ A trailing slash appears only on a field the API returns without a path, so on `
272
272
273
273
A URL field whose value isn't a valid absolute URL now fails response validation and throws <ApiLinkto="class/ResponseValidationError">`ResponseValidationError`</ApiLink>, the same as any other field that doesn't match the specification.
274
274
275
+
## `batchAddRequests()` rejects an oversized request before sending anything
276
+
277
+
<ApiLinkto="class/RequestQueueClient#batchAddRequests">`batchAddRequests()`</ApiLink> now measures the whole input before it sends the first batch. A request too large for the payload limit still throws the same error, and the message still names the request's index. What changed is that nothing has been sent by the time it throws. In v2 each batch was measured as it came up, so every batch before the oversized request had already gone out.
278
+
279
+
```js
280
+
// In v2 the four batches before the oversized request had already been sent.
Code that treated the throw as "some of these landed" and reconciled the queue afterwards can drop the reconciliation.
286
+
287
+
## A string body with a JSON content type is sent as it is
288
+
289
+
A string body sent with an explicit `application/json` content type now goes out verbatim. Axios used to parse it to check that it was valid JSON, and wrapped it in a JSON string literal when it wasn't. The client skips that step so that `batchAddRequests()` can send a body it has already serialized.
290
+
291
+
<ApiLinkto="class/KeyValueStoreClient#setRecord">`setRecord()`</ApiLink> is where you'd notice. Storing a non-JSON string under `contentType: 'application/json'` used to save `"my value"`, quotes included, and now saves `my value`, which <ApiLinkto="class/KeyValueStoreClient#getRecord">`getRecord()`</ApiLink> can't parse back:
292
+
293
+
```js
294
+
// v2 stored `"my value"`, v3 stores `my value`.
295
+
awaitclient.keyValueStore('my-store').setRecord({
296
+
key:'my-key',
297
+
value:'my value',
298
+
contentType:'application/json',
299
+
});
300
+
```
301
+
302
+
Give a record a content type matching what it holds, such as `text/plain`, or pass the value as an object and let the client serialize it.
303
+
304
+
The same applies to a string input passed together with a `contentType` to <ApiLinkto="class/ActorClient#start">`start()`</ApiLink>, <ApiLinkto="class/ActorClient#call">`call()`</ApiLink>, or <ApiLinkto="class/RunClient#metamorph">`metamorph()`</ApiLink>. v2 handed the API the string wrapped in quotes, v3 hands it over as it is.
305
+
275
306
## Timeouts are configured per tier
276
307
277
308
The `timeoutSecs` option of the <ApiLinkto="class/ApifyClient">`ApifyClient`</ApiLink> constructor is gone. Every method takes its timeout from one of three tiers, and the constructor sets the duration of each tier, along with a cap on any single request attempt:
0 commit comments