Skip to content
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

async_hooks: promise callbacks have no context unless init hook present #18520

Closed
ofrobots opened this issue Feb 2, 2018 · 8 comments · Fixed by #18540
Closed

async_hooks: promise callbacks have no context unless init hook present #18520

ofrobots opened this issue Feb 2, 2018 · 8 comments · Fixed by #18540
Labels
async_hooks Issues and PRs related to the async hooks subsystem. promises Issues and PRs related to ECMAScript promises.

Comments

@ofrobots
Copy link
Contributor

ofrobots commented Feb 2, 2018

While going through some examples I ran into the following buggy behavior in async_hooks. Promise callbacks don't seem to have context by default. Even more surprisingly Adding a dummy init makes the context appears as if by magic!

I intend to look at this in detail later, but opening the issue now in case someone else wants to take a peek.

const ah = require('async_hooks');

let hooks;
switch (process.argv[2]) {
  default:
  console.log('run this test with a numeric argument');
  process.exit(1);

  case '1':
    hooks = {}; // no hooks
    break;

  case '2':
    hooks = { init() {} }; // empty init hook
    break;
}

ah.createHook(hooks).enable();
Promise.resolve(1729).then(() => {
  console.log(`then callback ran with eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`);
});

Unlike timers, and other Node.js async APIs (e.g. fs.read), by default the triggerAsyncId is always 0 (i.e. missing context) inside the then callback. The behaviour is the same when no hooks are present or an empty set of hooks are present.

❯ node p2.js 1
then callback ran with eid 1 tid 0

Attaching an empty init hook magically makes the context appear:

❯ node p2.js 2
then callback ran with eid 7 tid 6

Replacing the promise example with a setTimeout or fs.readFile has the things working correctly in all scenarios.

// the rest of the code is the same
setTimeout(() => {
  console.log(`timeout callback ran with eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`);
}, 1);
~/tmp/ah
❯ node p2.js 1
timeout callback ran with eid 6 tid 1

~/tmp/ah
❯ node p2.js 2
timeout callback ran with eid 6 tid 1
@ofrobots
Copy link
Contributor Author

ofrobots commented Feb 2, 2018

/cc @nodejs/diagnostics @nodejs/async_hooks

@AndreasMadsen
Copy link
Member

@ofrobots Yep, it is a known issue. It is because PromiseWrap isn't setup unless a hook is used and for executionAsyncId or triggerAsyncId we need context information form PromiseWrap.

Unfortunately, we can't enable PromiseWrap constantly because assigning the asyncId to the promise resource object is too expensive.

As I have suggested previously, it would be nice if PromiseWrap allowed us to assign an asyncId and replaced the parentPromise with triggerAsyncId.

@ofrobots
Copy link
Contributor Author

ofrobots commented Feb 2, 2018

Yep, got it. The overhead of promise hooks is quite a bummer indeed. I agree that we should not add the promise hook overhead to applications that are not interested in monitoring asynchronous flow. This was not clear in the docs, so I have gone ahead and opened #18540 to fix this.

@ofrobots
Copy link
Contributor Author

ofrobots commented Feb 2, 2018

/cc @gsathya, @bmeurer for the feedback above about assigning asyncId.

@TimothyGu TimothyGu added promises Issues and PRs related to ECMAScript promises. async_hooks Issues and PRs related to the async hooks subsystem. labels Feb 4, 2018
@bmeurer
Copy link
Member

bmeurer commented Feb 5, 2018

If we reorganize the PromiseHook API, we could teach V8 to assign these IDs for all promises, always (at least when running in Node).

@gsathya
Copy link
Member

gsathya commented Feb 5, 2018

Unfortunately, we can't enable PromiseWrap constantly because assigning the asyncId to the promise resource object is too expensive.

Sorry for not having the complete context here, but why is assigning an ID to a promise object too expensive?

ofrobots added a commit to ofrobots/node that referenced this issue Feb 5, 2018
AsyncHooks have a few subtleties with being able to track promises.
This commit adds a section to the docs that explains things the issues.

PR-URL: nodejs#18540
Fixes: nodejs#18520
Reviewed-By: James M Snell <[email protected]>
@ofrobots
Copy link
Contributor Author

ofrobots commented Feb 5, 2018

@gsathya assigning the id requires us to enable promise hooks; which are expensive. Basically we need the init hook to be called so that we can assign an id to the promise, and record the trigger (causal) context.

@gsathya
Copy link
Member

gsathya commented Feb 5, 2018

Ah, so enabling promise hooks to know when to assign an ID is expensive. Assigning an ID isn't.

MylesBorins pushed a commit that referenced this issue Feb 21, 2018
AsyncHooks have a few subtleties with being able to track promises.
This commit adds a section to the docs that explains things the issues.

PR-URL: #18540
Fixes: #18520
Reviewed-By: James M Snell <[email protected]>
MylesBorins pushed a commit that referenced this issue Feb 21, 2018
AsyncHooks have a few subtleties with being able to track promises.
This commit adds a section to the docs that explains things the issues.

PR-URL: #18540
Fixes: #18520
Reviewed-By: James M Snell <[email protected]>
MylesBorins pushed a commit that referenced this issue Feb 21, 2018
AsyncHooks have a few subtleties with being able to track promises.
This commit adds a section to the docs that explains things the issues.

PR-URL: #18540
Fixes: #18520
Reviewed-By: James M Snell <[email protected]>
gibfahn pushed a commit that referenced this issue Apr 13, 2018
AsyncHooks have a few subtleties with being able to track promises.
This commit adds a section to the docs that explains things the issues.

PR-URL: #18540
Fixes: #18520
Reviewed-By: James M Snell <[email protected]>
MayaLekova pushed a commit to MayaLekova/node that referenced this issue May 8, 2018
AsyncHooks have a few subtleties with being able to track promises.
This commit adds a section to the docs that explains things the issues.

PR-URL: nodejs#18540
Fixes: nodejs#18520
Reviewed-By: James M Snell <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
async_hooks Issues and PRs related to the async hooks subsystem. promises Issues and PRs related to ECMAScript promises.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants