Rebuild: the worker joins stream pieces once per frame, gzips posts off its thread

decodeFrames joined each network piece (~64 KB) onto the frame so far:
a chunk of MBs copied over and over, 5.4% of a worker's time in an
end-to-end profile (the bench reads files, so it never showed). Pieces
are now joined once a frame is complete. A batch post's gzip (1.6%) runs
on a thread-pool thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Daan MeijerandClaude Opus 5.5 committed 2026-10-09 03:06:17 +02:00
1 parent bd1c846f27
commit 168605148f
3 files changed
+40 -12

No files matched your search

+25 -9
View File
@@ -26,17 +26,33 @@ export function encodeFrame(frame: Frame): Buffer {
return Buffer.concat([head, frame.data])
}
/** The frames of a byte stream, as they complete. */
/**
* The frames of a byte stream, as they complete. Pieces are kept in a list
* and joined once a frame is complete: joining each piece onto what came
* before copied a chunk of MBs once per 64 KB (~5% of a worker's time;
* LOG.md 2026-10-09).
*/
export async function* decodeFrames(stream: AsyncIterable<Uint8Array> | Iterable<Uint8Array>): AsyncGenerator<Frame> {
let buffer = Buffer.alloc(0)
let pieces: Buffer[] = []
let length = 0
/** Bytes needed before the next frame can be cut: its header first, then all of it. */
let needed = HEADER
for await (const piece of stream) {
buffer = buffer.length === 0 ? Buffer.from(piece) : Buffer.concat([buffer, piece])
while (buffer.length >= HEADER) {
const length = buffer.readUInt32BE(16)
if (buffer.length < HEADER + length) break
yield { from: Number(buffer.readBigUInt64BE(0)), to: Number(buffer.readBigUInt64BE(8)), data: buffer.subarray(HEADER, HEADER + length) }
buffer = buffer.subarray(HEADER + length)
pieces.push(Buffer.isBuffer(piece) ? piece : Buffer.from(piece.buffer, piece.byteOffset, piece.byteLength))
length += piece.byteLength
while (length >= needed) {
let buffer = pieces.length === 1 ? pieces[0] : Buffer.concat(pieces, length)
pieces = [buffer]
if (needed === HEADER) {
needed = HEADER + buffer.readUInt32BE(16)
if (length < needed) break
}
yield { from: Number(buffer.readBigUInt64BE(0)), to: Number(buffer.readBigUInt64BE(8)), data: buffer.subarray(HEADER, needed) }
buffer = buffer.subarray(needed)
pieces = buffer.length > 0 ? [buffer] : []
length = buffer.length
needed = HEADER
}
}
if (buffer.length > 0) throw new Error(`Stream ended inside a frame (${buffer.length} bytes left)`)
if (length > 0) throw new Error(`Stream ended inside a frame (${length} bytes left)`)
}