Hey there!
Noticed a memory leak in one of our services due to upgrading to V2 of hono node server.
After some lengthy profiling/general troubleshooting, we determined the issue was stemming from our usage of c.req.raw.clone().json(); when removed, the memory leak went away.
cert environment: before upgrade, during upgrade, after root cause fix (note: the spikes are nightly stress tests)
production: steady linear increase (with dropoffs every deploy) until fix put in place
We've fixed it from our side, but wanted to create this issue for visibility.
// truncated code of our before state for brevity (see: logger middleware at bottom)
export class GraphQLRoute extends Hono {
constructor(db: DBClient) {
super();
this.on(['POST', 'GET'], GRAPHQL_ENDPOINT, async (c) => {
const yoga = createYoga({
schema: buildSchema(db),
graphqlEndpoint: GRAPHQL_ENDPOINT,
});
return yoga.fetch(c.req.raw, c);
});
}
}
export class Server extends Hono {
constructor(db: DBClient) {
super();
this.use(loggerMiddleware({logger}))
.route('', new GraphQLRoute(db))
}
}
export const loggerMiddleware = (opts: {logger: typeof logger}) => {
return createMiddleware(async (c, next) => {
const loggerContext: Partial<Record<LoggableField, LoggableValue>> = {
payload: await c.req.raw.clone().json(), // we have to clone otherwise the stream will already have been read downstream and error
};
await opts.logger.runInContext(loggerContext, async () => {/* ... */});
});
};
After narrowing down the code via datadog profiling, copilot had this explanation if it's worth anything:
In @hono/node-server v2, the underlying Request body is a ReadableStream backed by the Node.js socket. Request.clone() calls ReadableStream.tee(), which buffers all chunks internally until both branches are consumed. The logger middleware consumes the clone branch immediately, but the original branch isn't consumed until Yoga/REST handlers run later (after next()). In v2, this tee buffer holds the full body in memory far longer than v1's implementation did — the +86 MiB the flame graph attributes to this file
Hey there!
Noticed a memory leak in one of our services due to upgrading to V2 of hono node server.
After some lengthy profiling/general troubleshooting, we determined the issue was stemming from our usage of
c.req.raw.clone().json(); when removed, the memory leak went away.We've fixed it from our side, but wanted to create this issue for visibility.
After narrowing down the code via datadog profiling, copilot had this explanation if it's worth anything: