Skip to content

fix(peer): stop leaking pooled buffer memory over structured-clone transports - #145

Merged
dinwwwh merged 2 commits into
mainfrom
claude/zen-franklin-bsxvla
Oct 7, 2026
Merged

dinwwwh merged 2 commits into
mainfrom
claude/zen-franklin-bsxvla

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Oct 7, 2026

Copy link
Copy Markdown
Member

Summary

This change optimizes octet stream transmission by ensuring that only the actual data being sent is transmitted to remote peers, rather than the entire backing ArrayBuffer when chunks are views into larger pooled buffers (common in Node.js).

Key Changes

  • Added toStandaloneBytes() utility function that detects when a Uint8Array is a view into a larger buffer and creates a copy containing only the relevant data
  • Updated OctetStreamTransmitter.transmit() to call toStandaloneBytes() on all binary chunks before sending, preventing structured-clone from serializing unused buffer memory
  • Added comprehensive test case verifying that:
    • Chunks from Node's pooled buffers are copied to standalone arrays
    • Whole-buffer chunks are sent as-is without unnecessary copying
    • The transmitted data is correct in both cases

Implementation Details

  • The toStandaloneBytes() function checks if byteLength === buffer.byteLength to determine if the array is a standalone view
  • Uses new Uint8Array(bytes) to create a copy rather than bytes.slice() since Buffer#slice() returns a view, not a copy
  • This optimization is particularly important for Node.js environments where buffers are often allocated from shared pools

https://claude.ai/code/session_011cCB5ionSFz6U6a1SUr1T8

claude added 2 commits October 7, 2026 03:04
`OctetStreamTransmitter` forwarded each chunk as-is. A chunk that is a view
onto a larger ArrayBuffer (Node pooled `Buffer` slices from `Buffer.from`,
`Buffer.concat`, `allocUnsafe`, `subarray`) sent over a transport that
structured-clones raw messages (Worker, MessagePort, Electron IPC) cloned the
whole backing buffer to the receiver, including unrelated pooled data.

Copy a chunk when its view doesn't cover its whole buffer. Chunks that already
own their buffer are still sent without a copy.

The other binary send paths are unaffected: atomic bodies are always Blobs, and
`encodePeerMessage` copies into a fresh frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011cCB5ionSFz6U6a1SUr1T8
Drop the `byteOffset === 0` clause (implied by a view covering its whole
buffer), shorten the helper's doc comment, and fold the two transmitter tests
into one that covers both the copy and the pass-through branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011cCB5ionSFz6U6a1SUr1T8
@pkg-pr-new

pkg-pr-new Bot commented Oct 7, 2026

Copy link
Copy Markdown
@standard-server/aws-lambda

npm i https://pkg.pr.new/@standard-server/aws-lambda@145

@standard-server/core

npm i https://pkg.pr.new/@standard-server/core@145

@standard-server/fastify

npm i https://pkg.pr.new/@standard-server/fastify@145

@standard-server/fetch

npm i https://pkg.pr.new/@standard-server/fetch@145

@standard-server/node

npm i https://pkg.pr.new/@standard-server/node@145

@standard-server/peer

npm i https://pkg.pr.new/@standard-server/peer@145

@standard-server/shared

npm i https://pkg.pr.new/@standard-server/shared@145

commit: b72bd8d

@codecov

codecov Bot commented Oct 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 108 skipped benchmarks1


Comparing claude/zen-franklin-bsxvla (b72bd8d) with main (2f06dd6)2

Open in CodSpeed

Footnotes

  1. 108 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

  2. No successful run was found on main (1053d1f) during the generation of this report, so 2f06dd6 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

  • Standalone-bytes copy in OctetStreamTransmitter — toStandaloneBytes() returns a chunk unchanged when it covers its whole backing ArrayBuffer (byteLength === buffer.byteLength, which implies byteOffset === 0) and otherwise copies with new Uint8Array(bytes); applied to every binary chunk in transmit() (packages/peer/src/octet-stream.ts:39, :83).
  • Regression test — feeds a pooled Buffer.from('hello') and a whole-buffer Uint8Array through a ReadableStream, asserting the pooled chunk is copied to an exactly-sized buffer (the discriminating buffer.byteLength === byteLength check) while the whole-buffer chunk is passed by identity (packages/peer/src/octet-stream.test.ts:146).

The premise checks out: structuredClone(Buffer.from('hello')) produces a clone with buffer.byteLength === 65536 while byteLength === 5, so raw structured-clone senders do ship the whole pool. StandardBody only surfaces binary as Blob or ReadableStream<Uint8Array>, and encodeAtomicStandardBody never emits a pooled Uint8Array for atomic bodies, so the octet stream is the only pool-exposed path and both peers route it through this transmitter. new Uint8Array(bytes) is the right copy (.slice() on a Buffer returns a view, as the comment notes).

Verified locally: pnpm --filter @standard-server/peer exec vitest run (261 passed) and tsc -b (clean).

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@dinwwwh dinwwwh changed the title Avoid sending pooled buffer backing memory in octet stream fix(peer): stop leaking pooled buffer memory over structured-clone transports Oct 7, 2026
@dinwwwh
dinwwwh merged commit d5d0eb6 into main Oct 7, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants