async_hooks: discourage AsyncLocalStorage.disable#58065
async_hooks: discourage AsyncLocalStorage.disable#58065legendecas wants to merge 1 commit intonodejs:mainfrom
Conversation
--async-context-frame
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #58065 +/- ##
==========================================
- Coverage 90.21% 90.21% -0.01%
==========================================
Files 630 630
Lines 186391 186397 +6
Branches 36610 36612 +2
==========================================
+ Hits 168146 168150 +4
+ Misses 11066 11053 -13
- Partials 7179 7194 +15
🚀 New features to boost your workflow:
|
When `--async-context-frame` is enabled (and by default in v24), there should be no need to manually call `asyncLocalStorage.disable()`.
Qard
left a comment
There was a problem hiding this comment.
The disable() function is not just for letting the store instance be GC'd, it's also to remove the associated entry from the AsyncContextFrame, allowing the value to be GC'd too. This is still a valid use case.
With this change, the symbol is held alive instead, and the value remains in the map.
This statement contradicts with the document: Lines 228 to 230 in fc054bb The document says On the tip of main, the behavior also aligns with the doc. It can be verified with a test script: // Flags: --async-context-frame --expose-gc
'use strict';
const common = require('../common');
const { AsyncLocalStorage } = require('async_hooks');
const { gcUtil, gcUntil } = require('../common/gc');
let weakRef;
{
const als = new AsyncLocalStorage();
let obj = {};
als.run(obj, () => {
setInterval(() => {}, 1000).unref();
});
als.disable();
weakRef = new WeakRef(obj);
obj = null;
}
// This assertion fails.
gcUntil('als value', () => {
return weakRef.deref() == null;
}).then(common.mustCall());That is, with |
| provided by the `asyncLocalStorage`, as those objects are garbage collected | ||
| along with the corresponding async resources. | ||
| There is no need to call this method in order to get an `asyncLocalStorage` | ||
| instance from being garbage-collected. |
There was a problem hiding this comment.
The wording here is a bit off. If the intention is to say that disable() is not needed to allow the ALS instance to be garbage collected then I would actually suggest just dropping this first sentence.
There was a problem hiding this comment.
This only works with --async-context-frame. Given that --async-context-frame is the default option now, it is simply discouraged to use disable.
| `asyncLocalStorage.disable()` is required before the `asyncLocalStorage` itself | ||
| can be garbage collected. However, this does not apply to stores provided by | ||
| the `asyncLocalStorage`, as those objects are garbage collected along with the | ||
| corresponding async resources. |
There was a problem hiding this comment.
This second sentence here is rather confusing if you're not familiar with ALS as a whole. I'm not sure it adds a lot of value here.
There was a problem hiding this comment.
It is an important trait of disable that it does not make all entered stores GC-able. It is an existing document sentence that re-organized in this PR:
Lines 228 to 230 in fc054bb
|
I'm still not really clear what the expectation of I doubt we find a way to remove all stores from all In the old implementation In the new implementation |
|
Depends on the use, but yes, it's not a great API. It's really only implemented to try to keep feature parity with the async_hooks implementation. But the actual usefulness is debatable. More could probably be done to bring the behaviour in line with the async_hooks implementation, but discouraging its use or possibly even deprecating it may also be reasonable. I would say it's not quite the same as |
|
I agree Having an API to just remove the strong reference from the current active I fear a global working |
|
Given that it's marked experimental currently, and the ergonomics are...not the best...I think it's probably fine to warn against its use, but perhaps provide more clarity about why it continues to exist rather than just being entirely deprecated? Could also just deprecate it anyway, as I'm not sure if anyone is even using it. 🤔 |
When
--async-context-frameis enabled (and by default in v24), thereshould be no need to manually call
asyncLocalStorage.disable().Nevertheless, a general
AsyncLocalStorageuse case will construct anAsyncLocalStorageat module-top-level, so it will never get GC-ed anyway.If calling
asyncLocalStorage.disable()and enable it again, issues like#53037 will rise. So its use should be discouraged.
Refs: #58019
/cc @nodejs/diagnostics