Backend Development 15 min read

Managing Request Context in Node.js with async_hooks without Monkey Patching

This article explains how to propagate request‑level context in a Node.js HTTP service by building a call‑tree with async_hooks, avoiding monkey‑patching, handling lifecycle quirks, preventing memory leaks, and using reference‑counted cleanup to ensure reliable context queries.

Bitu Technology
Bitu Technology
Bitu Technology
Managing Request Context in Node.js with async_hooks without Monkey Patching

Overview

The article continues a previous discussion on using async_hook to pass request context through business logic and explores a solution that does not rely on monkey‑patching. It demonstrates how to construct a call‑tree from async IDs, set and query context, and manage the lifecycle of async resources.

Call‑Stack Technique

By visualising the call‑stack as a dependency tree, each node represents a function call. Middleware placed at the beginning of the HTTP stack appears in the lower‑left part of the tree, executing before later calls.

Building the Tree

In the async_hook callback, a new node is created for every asyncId that triggers another asyncId , mirroring the diagram shown in the original article.

Setting Context

A dedicated middleware is inserted as the first element in the middleware chain. For each incoming request it:

Assigns the required context to the current asyncId .

Walks up the ancestor chain; if an ancestor lacks context, it copies the newly set context to that ancestor.

The algorithm runs in O(1) average time because the number of recursive look‑ups never exceeds the call depth.

Querying Context

To retrieve context, the algorithm starts from the current asyncId and climbs the parent chain until it finds a node with stored context, copying it to each visited node on the way back.

Advantages and Disadvantages

Advantages: no dependency on specific libraries, same computational complexity as monkey‑patching, and avoids invasive patches.

Disadvantages: more complex implementation, may fail if two independent requests share the same async resource, and requires careful reference management to prevent memory leaks.

Lifecycle Issues with async_hooks

The article points out two surprising behaviours observed in Node.js 10:

Some async resources emit before / after callbacks while others do not.

Ancestor destroy events can fire before descendant before / after events.

These quirks can leave the top of the call‑stack without associated context if the context is cleared on destroy . To avoid memory leaks, the article proposes a reference‑counted cleanup:

init

When a node is created, its context is marked as referenced and the parent’s reference count is incremented.

destroy

Remove the node’s own reference.

If no descendants reference the context, delete it and decrement the parent’s count.

Repeat the check up the tree.

Practical Problems Observed in Production

Long‑running keep‑alive HTTP connections prevented destroy from being called, causing a steady increase of unreclaimed async resources. The authors mitigated this with a generational garbage‑collection approach using two maps (old and new) and periodically swapping them every five minutes.

Conclusion

Using async_hook for context propagation simplifies tracing and statistics in micro‑service environments. The full implementation is available in the open‑source project envoy-node , and readers are encouraged to provide feedback.

backendmiddlewareNode.jsmemory-leakReference Countingasync_hookscontext propagation
Bitu Technology
Written by

Bitu Technology

Bitu Technology is the registered company of Tubi's China team. We are engineers passionate about leveraging advanced technology to improve lives, and we hope to use this channel to connect and advance together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.