perf: serialize an RPC request into one buffer - #1776
Conversation
`RpcRequestPayload` wrote a small buffer per parameter and yielded each in turn. It now writes the whole request — header, and every parameter's header, TYPE_INFO and value — into a single `WritableTrackingBuffer` and yields its chunks once. The bytes are unchanged, and a large value written by reference is still referenced rather than copied (the tracking buffer references buffers of 8 KB or more), so this adds no copy; the request just reaches the packetizer as a few large chunks instead of a small buffer per parameter. Serializing a 20-scalar-parameter request is about 1.7x faster (benchmarks/parameters/scalar-params.js); large binary values, dominated by the value itself, are unchanged. The existing byte-equivalence and by-reference tests cover it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fxv5h4UMCGJEpjgCKcAxug
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Reviewed the diff ( Summary: Clean, well-scoped change. Collapsing per-parameter Correctness
Minor/nit
No security or performance concerns beyond what's already covered by the PR's own benchmarks, which look credible (fewer small-buffer allocations and generator yields for scalar-heavy requests, no change for large binary values). Test coverage looks adequate for this internal refactor; nothing new needed given the byte-equivalence suite already exists and covers this path. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 687b8e030d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const parametersLength = this.parameters.length; | ||
| for (let i = 0; i < parametersLength; i++) { | ||
| yield * this.generateParameterData(this.parameters[i]); | ||
| this.writeParameterData(buffer, this.parameters[i]); | ||
| } |
There was a problem hiding this comment.
Preserve streaming between large parameters
When an RPC contains multiple large string or TVP parameters, this loop now serializes every parameter before the first chunk is yielded. WritableTrackingBuffer retains all generated chunks until getBuffers() finishes, so encoded strings and TVP rows for the entire request remain resident simultaneously; previously each parameter's chunks could be consumed and released before the next parameter was generated. This can substantially increase peak memory and block the event loop, potentially causing OOM failures for large multi-parameter requests. Yield or consume completed chunks incrementally rather than buffering the full RPC.
Useful? React with 👍 / 👎.
|
Folded into #1777, which already carries this commit (687b8e0) and whose description covers the single-buffer serialization. Keeping a 9-line stepping stone as its own PR only adds a CI cycle to the path to streaming parameters, so this is closed without merging. Generated by Claude Code |
Problem
RpcRequestPayloadwrites a smallWritableTrackingBufferper parameter and yields each one in turn: a 20-parameter request reaches the packetizer as ~21 separate buffers. Each buffer and each yield is overhead, and downstreamReadable.from(payload)does a pull cycle per chunk.Change
The payload now writes the whole request — the request header, and every parameter's header, TYPE_INFO and value — into a single
WritableTrackingBuffer, and yields its chunks once at the end.The bytes are unchanged. A large value written by reference is still referenced, not copied:
WritableTrackingBufferkeeps buffers of 8 KB or more as their own chunks, sogetBuffers()still hands the value out as a distinct by-reference chunk. So this adds no copy — the request just arrives as a few large chunks instead of a small buffer per parameter. Per-parameter error attribution is unchanged: each parameter'swriteTypeInfo/writeValueis still wrapped in theInputErrorthat names it.Measurements
Serialization only, same machine,
benchmarks/parameters/scalar-params.js(20 scalar parameters per request):About 1.7x for scalar-heavy requests; binary values, dominated by the value itself, are unchanged.
Validation
test/unit/rpcrequest-payload-test.ts: 40 parameter cases across TDS 7.4/7.2, with/without collation) and the large-value by-reference test cover this unchanged.Stacked on #1774; retargets to
masteronce that merges. It also sets up the streaming-parameter work to follow: the payload becomes the one place that decides how a request is written out.🤖 Generated with Claude Code
https://claude.ai/code/session_01Fxv5h4UMCGJEpjgCKcAxug
Generated by Claude Code