-
Notifications
You must be signed in to change notification settings - Fork 30.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revisit the AsyncLocalStorage onPropagate option #46374
Comments
Here you wrote anyhow, the main usecase I had in mind for In the To my understanding the |
The key point is that For the |
You assume here that an ALS user has full control over the complete code which is usually not true. If one has full control over the code ALS would be not needed because one can manually pass the context around (like done in go). If e.g. some database driver internally does |
Yep, I understand that. I think this is a more general problem that the |
It definitely not compatible with the current
Personally, I think async function test() {
const before = als.getStore();
await 1;
const after = als.getStore();
assert.strictEqual(before, after);
} |
Are there any ideas how to cover the use cases mentioned above without |
With the ability to single out If you're ok with a general timeout, you could implement something using an intermediate holder: type Intermediate<T> = {
value: T | undefined;
};
export class TimedContext<T> extends AsyncContext<Intermediate<T>> {
get(): T | undefined {
return super.get()?.value;
}
run(value: T, cb: (...args: any[]) => any, ...args: any[]): any {
const intermediate = { value: value };
setTimeout(() => {
intermediate.value = undefined;
}, 10 * 60 * 60 * 1000);
return super.run(intermediate, cb, ...args);
}
} |
It's not concrete about setInterval, this is just an example where a store gets captured forever. It's about the possibility to break/stop propagation based on "some" condition. And the point is to do this for one Please note that AsyncLocalStore instances are in most cases created independent of the remaining application (e.g. like in domain module in node.js). The actual code executed later is out of reach to tune it. I think the best workaround is likely to wrap the actual store in a proxy object and clear it in a |
Given the conversation here, and given that |
I didn't see how this "tag" model would solve the problem. Aside from passing the
Binding to the root context avoids capturing the store values. I didn't find a real-world example of filtered propagation, would you mind sharing one if possible? |
Let's forget about |
Refs: nodejs#46374 The `onPropagate` option for `AsyncLocalStorage` is problematic for a couple of reasons: 1. It is not expected to be forwards compatible in any way with the upcoming TC-39 `AsyncContext` proposal. 2. It introduces a non-trivial O(n) cost invoking a JavaScript callback for *every* AsyncResource that is created, including every Promise. While it is still experimental, I recommend removing it while we can revisit the fundamental use cases in light of the coming `AsyncContext` proposal.
Binding to root requires access/modifications at the actual code location where the new AsyncResource is crated. For monitoring tools like OTel instrumentations the actual application code and the owner of ALS instance are not the same therefore this is hard to archive. e.g. HTTP server instrumentation wraps the HTTP request handler and calls the original with a store set but the HTTP instrumentation can't know/modify the actual user code in the original request handler. The discussion here touches also this topic but very specific to timers wheres my idea with Anyhow, adding |
Refs: nodejs#46374 The `onPropagate` option for `AsyncLocalStorage` is problematic for a couple of reasons: 1. It is not expected to be forwards compatible in any way with the upcoming TC-39 `AsyncContext` proposal. 2. It introduces a non-trivial O(n) cost invoking a JavaScript callback for *every* AsyncResource that is created, including every Promise. While it is still experimental, I recommend removing it while we can revisit the fundamental use cases in light of the coming `AsyncContext` proposal.
Thinking more about the binding to root case, if we do adopt the const als = new AsyncLocalStorage();
const runInRootContext = AsyncLocalStorage.snapshotRoot();
runInRootContext (() => {
als.run(123, () => {
// ...
});
}); |
But as said usually code where propagation is triggered and owners of the als instances are not the same therefore explicit calls on all these als instances are not an option. |
The `onPropagate` option for `AsyncLocalStorage` is problematic for a couple of reasons: 1. It is not expected to be forwards compatible in any way with the upcoming TC-39 `AsyncContext` proposal. 2. It introduces a non-trivial O(n) cost invoking a JavaScript callback for *every* AsyncResource that is created, including every Promise. While it is still experimental, I recommend removing it while we can revisit the fundamental use cases in light of the coming `AsyncContext` proposal. Refs: #46374 PR-URL: #46386 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
|
The `onPropagate` option for `AsyncLocalStorage` is problematic for a couple of reasons: 1. It is not expected to be forwards compatible in any way with the upcoming TC-39 `AsyncContext` proposal. 2. It introduces a non-trivial O(n) cost invoking a JavaScript callback for *every* AsyncResource that is created, including every Promise. While it is still experimental, I recommend removing it while we can revisit the fundamental use cases in light of the coming `AsyncContext` proposal. Refs: #46374 PR-URL: #46386 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
The `onPropagate` option for `AsyncLocalStorage` is problematic for a couple of reasons: 1. It is not expected to be forwards compatible in any way with the upcoming TC-39 `AsyncContext` proposal. 2. It introduces a non-trivial O(n) cost invoking a JavaScript callback for *every* AsyncResource that is created, including every Promise. While it is still experimental, I recommend removing it while we can revisit the fundamental use cases in light of the coming `AsyncContext` proposal. Refs: #46374 PR-URL: #46386 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
The
onPropagate
option was landed as experimental in v19.2... I'd like to propose that we revisit that and take a different approach.https://nodejs.org/dist/latest-v19.x/docs/api/async_context.html#new-asynclocalstorageoptions
As background,
onPropagate
attaches a callback function to theAsyncLocalStorage
instance that is invoked whenever a newAsyncResource
is created. This is problematic primarily because it incurs a significant additional performance overhead and assumes a model that is based on the underlying async_hooks API model (which I think there's consensus we want to move away from #46265).Alternatively, I'd like to propose a "tag' model like in the following example:
The
tag
here effectively becomes a component of the storage key. This is much more efficient design that will not incur a significant additional performance penalty./cc @legendecas @littledan @nodejs/async_hooks
The text was updated successfully, but these errors were encountered: