Skip to content

perf!: serialize requests once in batchAddRequests - #1051

Merged
vdusek merged 10 commits into
v3from
perf/batch-add-requests-single-serialization
Sep 14, 2026
Merged

vdusek merged 10 commits into
v3from
perf/batch-add-requests-single-serialization

Conversation

@vdusek

@vdusek vdusek commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Description

  • Mirroring fix: Make batch_add_requests split batches by serialized payload size 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

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

@vdusek vdusek added the t-tooling Issues with this label are in the ownership of the tooling team. label Sep 9, 2026
@vdusek vdusek self-assigned this Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

⚠️ There are broken links in the documentation.

See more at https://github.com/apify/apify-client-js/actions/runs/34862046980#summary-104036487629

@vdusek vdusek changed the title perf: serialize requests once in batchAddRequests perf!: serialize requests once in batchAddRequests Sep 9, 2026
@vdusek
vdusek requested a review from barjin September 9, 2026 12:19
@vdusek
vdusek marked this pull request as ready for review September 9, 2026 12:19
@vdusek
vdusek requested a review from szaganek as a code owner September 9, 2026 12:19
…-single-serialization

# Conflicts:
#	docs/04_upgrading/upgrading_v3.md
#	docs/public-api/apify-client.api.md
#	src/resource_clients/request_queue.ts
#	test/http_client.test.ts
…-single-serialization

# Conflicts:
#	src/resource_clients/request_queue.ts

@barjin barjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some nits / thinking out loud, otherwise lgtm.

Thanks @vdusek !

Comment thread src/interceptors.ts Outdated

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.

<ApiLink to="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 <ApiLink to="class/KeyValueStoreClient#getRecord">`getRecord()`</ApiLink> can't parse back:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which getRecord() can't parse back

This is kinda annoying, but understandable.

I'm not sure whether we should automatically parse the KVS contents based on the content-type in the client, but that ship has sailed, I suppose.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm merging this. However, feel free to start a follow-up Slack discussion about it. I'd probably lean toward dropping this automatic parsing of KVS records based on their content type, but I think it deserves a bit more investigation and discussion first with a broader audience (also #product-dev-tools channel). I opened a follow-up issue for it: #1067, and we should discuss it before the v3 release.

@vdusek
vdusek merged commit 2ca4b04 into v3 Sep 14, 2026
8 checks passed
@vdusek
vdusek deleted the perf/batch-add-requests-single-serialization branch September 14, 2026 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants