From ba00875f0143ed4bd2f52cbcf4511e8ea326535d Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 16 Sep 2025 22:24:38 +0200 Subject: [PATCH] stream: use new AsyncResource instead of bind The bind method uses ObjectDefineProperty that shows up in flamegraphs. This changes it to avoid the utility. Signed-off-by: Matteo Collina PR-URL: https://github.com/nodejs/node/pull/59867 Reviewed-By: Robert Nagy Reviewed-By: Ruben Bridgewater Reviewed-By: Rafael Gonzaga Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Stephen Belanger --- lib/internal/streams/end-of-stream.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js index cb0a9242e05..9b4a0b9d6f3 100644 --- a/lib/internal/streams/end-of-stream.js +++ b/lib/internal/streams/end-of-stream.js @@ -45,7 +45,7 @@ const { } = require('internal/streams/utils'); // Lazy load -let AsyncLocalStorage; +let AsyncResource; let addAbortListener; function isRequest(stream) { @@ -54,6 +54,14 @@ function isRequest(stream) { const nop = () => {}; +function bindAsyncResource(fn, type) { + AsyncResource ??= require('async_hooks').AsyncResource; + const resource = new AsyncResource(type); + return function(...args) { + return resource.runInAsyncScope(fn, this, ...args); + }; +} + function eos(stream, options, callback) { if (arguments.length === 2) { callback = options; @@ -66,8 +74,9 @@ function eos(stream, options, callback) { validateFunction(callback, 'callback'); validateAbortSignal(options.signal, 'options.signal'); - AsyncLocalStorage ??= require('async_hooks').AsyncLocalStorage; - callback = once(AsyncLocalStorage.bind(callback)); + // Avoid AsyncResource.bind() because it calls ObjectDefineProperties which + // is a bottleneck here. + callback = once(bindAsyncResource(callback, 'STREAM_END_OF_STREAM')); if (isReadableStream(stream) || isWritableStream(stream)) { return eosWeb(stream, options, callback);