From 2456e6abb0b5acee97d607a692e0d2aa61437955 Mon Sep 17 00:00:00 2001 From: Dinh Le Date: Tue, 6 Oct 2026 10:41:14 +0700 Subject: [PATCH 1/2] chore: remove unreliable octet stream benchmarks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rpc and openapi link + handler octet stream benches flip between two values (~640 µs vs ~728 µs for rpc) across CI runs with no code changes, producing false ±12-14% CodSpeed regressions on unrelated PRs. Remove them and the payload helpers only they used. --- benches/__shared__/payloads.ts | 53 --------------------------- benches/openapi-link-handler.bench.ts | 8 +--- benches/rpc-link-handler.bench.ts | 8 +--- 3 files changed, 2 insertions(+), 67 deletions(-) diff --git a/benches/__shared__/payloads.ts b/benches/__shared__/payloads.ts index 4c984f62e..a6d15e337 100644 --- a/benches/__shared__/payloads.ts +++ b/benches/__shared__/payloads.ts @@ -34,9 +34,6 @@ function createUnit(i: number) { } } -const SIZE_1KB = 1024 -const SIZE_10KB = 10 * SIZE_1KB -const SIZE_100KB = 100 * SIZE_1KB const SIZE_5MB = 5 * 1024 * 1024 export const PAYLOAD_1KB = createUnit(0) @@ -60,25 +57,6 @@ export const EVENTS_10KB = Array.from({ length: 10 }).fill(PAYLOAD_1KB) export const EVENTS_100KB = Array.from({ length: 50 }).fill([PAYLOAD_1KB, PAYLOAD_1KB]) export const EVENTS_5MB = Array.from({ length: 1000 }).fill([PAYLOAD_1KB, PAYLOAD_1KB, PAYLOAD_1KB, PAYLOAD_1KB, PAYLOAD_1KB]) -function splitBytes(size: number, parts: number): Uint8Array[] { - const buf = new Uint8Array(size) - const base = Math.floor(size / parts) - let rem = size - base * parts - const out: Uint8Array[] = [] - let off = 0 - for (let i = 0; i < parts; i++) { - const n = base + (rem-- > 0 ? 1 : 0) - out.push(buf.subarray(off, off + n)) - off += n - } - return out -} - -export const BYTES_1KB = splitBytes(SIZE_1KB, 1) -export const BYTES_10KB = splitBytes(SIZE_10KB, 10) -export const BYTES_100KB = splitBytes(SIZE_100KB, 50) -export const BYTES_5MB = splitBytes(SIZE_5MB, 1000) - /** Fresh async generator over prebuilt event parts (one-shot per call). */ export function asSyncIteratorObject(parts: readonly unknown[]): AsyncGenerator { return (async function* () { @@ -88,21 +66,6 @@ export function asSyncIteratorObject(parts: readonly unknown[]): AsyncGenerator< }()) } -/** Fresh ReadableStream over prebuilt octet chunks (one-shot per call). */ -export function asReadableStream(parts: readonly Uint8Array[]): ReadableStream> { - let i = 0 - return new ReadableStream({ - pull(controller) { - if (i >= parts.length) { - controller.close() - } - else { - controller.enqueue(parts[i++]!) - } - }, - }) -} - export async function drainBody(body: unknown): Promise { if (body === undefined || body === null) { return @@ -116,21 +79,5 @@ export async function drainBody(body: unknown): Promise { break } } - return - } - - if (body instanceof ReadableStream) { - const reader = body.getReader() - try { - while (true) { - const { done } = await reader.read() - if (done) { - break - } - } - } - finally { - reader.releaseLock() - } } } diff --git a/benches/openapi-link-handler.bench.ts b/benches/openapi-link-handler.bench.ts index 23ce1e63f..7b21bef61 100644 --- a/benches/openapi-link-handler.bench.ts +++ b/benches/openapi-link-handler.bench.ts @@ -6,7 +6,7 @@ import { OpenAPIHandlerCodec, OpenAPILinkCodec } from '@orpc/openapi/standard' import { os, type } from '@orpc/server' import { StandardHandler } from '@orpc/server/standard' import { bench } from 'vitest' -import { asReadableStream, asSyncIteratorObject, BYTES_10KB, drainBody, EVENTS_10KB, handlers, PAYLOAD_10KB } from './__shared__/payloads' +import { asSyncIteratorObject, drainBody, EVENTS_10KB, handlers, PAYLOAD_10KB } from './__shared__/payloads' const serializer = new OpenAPISerializer({ handlers }) @@ -46,10 +46,4 @@ describe('openapi link + handler', () => { await client.ping(asSyncIteratorObject(EVENTS_10KB)), ) }) - - bench('octet stream', async () => { - await drainBody( - await client.ping(asReadableStream(BYTES_10KB)), - ) - }) }) diff --git a/benches/rpc-link-handler.bench.ts b/benches/rpc-link-handler.bench.ts index cdd36b9cd..298966ab8 100644 --- a/benches/rpc-link-handler.bench.ts +++ b/benches/rpc-link-handler.bench.ts @@ -4,7 +4,7 @@ import { RPCLinkCodec, StandardLink } from '@orpc/client/standard' import { os, type } from '@orpc/server' import { RPCHandlerCodec, StandardHandler } from '@orpc/server/standard' import { bench } from 'vitest' -import { asReadableStream, asSyncIteratorObject, BYTES_10KB, drainBody, EVENTS_10KB, handlers, PAYLOAD_10KB } from './__shared__/payloads' +import { asSyncIteratorObject, drainBody, EVENTS_10KB, handlers, PAYLOAD_10KB } from './__shared__/payloads' const serializer = new RPCSerializer({ handlers }) @@ -44,10 +44,4 @@ describe('rpc link + handler', () => { await client.ping(asSyncIteratorObject(EVENTS_10KB)), ) }) - - bench('octet stream', async () => { - await drainBody( - await client.ping(asReadableStream(BYTES_10KB)), - ) - }) }) From 93b943e826905622a3e6001625bca719ec057f06 Mon Sep 17 00:00:00 2001 From: Dinh Le Date: Tue, 6 Oct 2026 13:16:29 +0700 Subject: [PATCH 2/2] chore: keep ReadableStream draining in shared drainBody static-file-handler benches still pass ReadableStream response bodies to drainBody; without this branch they stopped reading the file and under-measured the serve path. --- benches/__shared__/payloads.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/benches/__shared__/payloads.ts b/benches/__shared__/payloads.ts index a6d15e337..54f29b72f 100644 --- a/benches/__shared__/payloads.ts +++ b/benches/__shared__/payloads.ts @@ -79,5 +79,21 @@ export async function drainBody(body: unknown): Promise { break } } + return + } + + if (body instanceof ReadableStream) { + const reader = body.getReader() + try { + while (true) { + const { done } = await reader.read() + if (done) { + break + } + } + } + finally { + reader.releaseLock() + } } }