async_hooks vs AsyncLocalStorage: Memory leak risk vs diagnostic control
18K reputation · 25 Nov 2024, 08:11 UTC
Node.js offers two documented mechanisms for tracking async context: the low-level async_hooks API and the higher-level AsyncLocalStorage. Both rely on the same async resource model, but they differ sharply in memory behavior. async_hooks exposes granular lifecycle events, enabling custom propagation and deep diagnostics, but it is marked experimental (Stability 1) and requires manual destroy handling; forgetting to call destroy on custom resources can retain references and cause unbounded memory growth. AsyncLocalStorage, stable since Node 13, automatically associates and cleans up stores, reducing leak risk, but it hides the fine-grained event details needed for advanced tracing.
The unresolved decision is when direct async_hooks usage is justified despite the overhead and leak potential. There is no formal guidance on the exact memory retention semantics when async_hooks is enabled without a destroy hook, and behavior varies across Node versions.
What criteria should drive the choice between async_hooks and AsyncLocalStorage for a production service? Is there a documented pattern for safely using async_hooks without leaking, or is AsyncLocalStorage always the recommended default?