From b8eb6acff14724ba8ca350a9f17b8f62e739ce1c Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Wed, 8 Oct 2025 11:06:13 +0200 Subject: [PATCH 1/3] perf(OpenNextResponse): do not store the chunks for streamed responses --- .changeset/honest-lamps-smell.md | 10 ++++++++++ packages/open-next/src/http/openNextResponse.ts | 7 +++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 .changeset/honest-lamps-smell.md diff --git a/.changeset/honest-lamps-smell.md b/.changeset/honest-lamps-smell.md new file mode 100644 index 000000000..06ab79201 --- /dev/null +++ b/.changeset/honest-lamps-smell.md @@ -0,0 +1,10 @@ +--- +"@opennextjs/aws": minor +--- + +perf(OpenNextResponse): do not store the chunks for streamed responses + +There is no need to store the chunks for streamed responses. +Not storing the chunks allows saving memory. + +Note that `OpenNextHandler` will now return an empty body for streamed responses. diff --git a/packages/open-next/src/http/openNextResponse.ts b/packages/open-next/src/http/openNextResponse.ts index 59f6b8eb8..9dbdd83f0 100644 --- a/packages/open-next/src/http/openNextResponse.ts +++ b/packages/open-next/src/http/openNextResponse.ts @@ -291,7 +291,10 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse { } private _internalWrite(chunk: any, encoding: BufferEncoding) { - this._chunks.push(Buffer.from(chunk, encoding)); + if (!this.streamCreator) { + // Do not keep chunks around for streamed responses + this._chunks.push(Buffer.from(chunk, encoding)); + } this.push(chunk, encoding); this.streamCreator?.onWrite?.(); } @@ -314,7 +317,7 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse { this.flushHeaders(); } // In some cases we might not have a store i.e. for example in the image optimization function - // We may want to reconsider this in the future, it might be intersting to have access to this store everywhere + // We may want to reconsider this in the future, it might be interesting to have access to this store everywhere globalThis.__openNextAls ?.getStore() ?.pendingPromiseRunner.add(this.onEnd(this.headers)); From dcfdb8854a8371a403a0896a611c0a9d5fed17f0 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Wed, 8 Oct 2025 13:16:48 +0200 Subject: [PATCH 2/3] fixup! length --- .../open-next/src/http/openNextResponse.ts | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/packages/open-next/src/http/openNextResponse.ts b/packages/open-next/src/http/openNextResponse.ts index 9dbdd83f0..45ff8425f 100644 --- a/packages/open-next/src/http/openNextResponse.ts +++ b/packages/open-next/src/http/openNextResponse.ts @@ -20,11 +20,13 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse { statusCode!: number; statusMessage = ""; headers: OutgoingHttpHeaders = {}; - private _cookies: string[] = []; - private responseStream?: Writable; headersSent = false; _chunks: Buffer[] = []; + private _cookies: string[] = []; + private responseStream?: Writable; + private bodyLength = 0; + // To comply with the ServerResponse interface : strictContentLength = false; assignSocket(_socket: Socket): void { @@ -282,18 +284,12 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse { return Buffer.concat(this._chunks); } - getBodyLength(): number { - let size = 0; - for (const chunk of this._chunks) { - size += chunk.length; - } - return size; - } - private _internalWrite(chunk: any, encoding: BufferEncoding) { + const buffer = Buffer.from(chunk, encoding); + this.bodyLength += buffer.length; if (!this.streamCreator) { // Do not keep chunks around for streamed responses - this._chunks.push(Buffer.from(chunk, encoding)); + this._chunks.push(buffer); } this.push(chunk, encoding); this.streamCreator?.onWrite?.(); @@ -321,8 +317,7 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse { globalThis.__openNextAls ?.getStore() ?.pendingPromiseRunner.add(this.onEnd(this.headers)); - const bodyLength = this.getBodyLength(); - this.streamCreator?.onFinish?.(bodyLength); + this.streamCreator?.onFinish?.(this.bodyLength); //This is only here because of aws broken streaming implementation. //Hopefully one day they will be able to give us a working streaming implementation in lambda for everyone @@ -331,7 +326,7 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse { //BE CAREFUL: Aws keeps rolling out broken streaming implementations even on accounts that had working ones before //This is not dependent on the node runtime used if ( - bodyLength === 0 && + this.bodyLength === 0 && // We use an env variable here because not all aws account have the same behavior // On some aws accounts the response will hang if the body is empty // We are modifying the response body here, this is not a good practice From db4fb190c32c981037cc10a2a4997e4caec1b0d1 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Wed, 8 Oct 2025 13:31:59 +0200 Subject: [PATCH 3/3] Update .changeset/honest-lamps-smell.md Co-authored-by: conico974 --- .changeset/honest-lamps-smell.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.changeset/honest-lamps-smell.md b/.changeset/honest-lamps-smell.md index 06ab79201..54278a33a 100644 --- a/.changeset/honest-lamps-smell.md +++ b/.changeset/honest-lamps-smell.md @@ -7,4 +7,5 @@ perf(OpenNextResponse): do not store the chunks for streamed responses There is no need to store the chunks for streamed responses. Not storing the chunks allows saving memory. -Note that `OpenNextHandler` will now return an empty body for streamed responses. +BREAKING CHANGE: Note that `OpenNextHandler` will now return an empty body if your wrapper provides a `StreamCreator` +This could break custom converters.