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: triggerAsyncId can be undefined #14387

Closed
wants to merge 2 commits into from

Conversation

mcollina
Copy link
Member

@mcollina mcollina commented Jul 20, 2017

Fixes: #14386
Fixes: #14381
Fixes: #14368

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

async_hooks

@nodejs-github-bot nodejs-github-bot added the process Issues and PRs related to the process subsystem. label Jul 20, 2017
@mcollina
Copy link
Member Author

@nodejs/async_hooks @AndreasMadsen please verify. I do not claim this is the right fix or what other consequences this fix could have, but it solves the problem here.

@mcollina mcollina added the async_hooks Issues and PRs related to the async hooks subsystem. label Jul 20, 2017
@mcollina
Copy link
Member Author

Side note: the commit does not pass core-validate-commit as it does not recognize 'async-hooks' as a valid subsystem.

@mcollina
Copy link
Member Author

This problem was likely introduced in #14026.

@AndreasMadsen
Copy link
Member

AndreasMadsen commented Jul 20, 2017

I do not claim this is the right fix or what other consequences this fix could have, but it solves the problem here.

We need to find the cause for why triggerAsyncId is undefined. Likely it is supposed to be null under some special condition that we aren't testing for.

@mcollina
Copy link
Member Author

@AndreasMadsen the way 'shot' works is by piggypacking on core, and it does not allocate a full socket. https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js#L281 is expecting to have a real socket, but it has not in this case.

I can write that test and default the value of that symbol to null there if it's the case, but I think this might be the cause of a lot of other breakages. We are expecting something to be there and it is not. Forcing the community to track down crashes like that is not what I define as stable or safe to use. async_hooks are experimental, but this is breaking code that are not using them.

This change avoid all the crashes, at the cost of reporting bad data, why is it wrong?

@AndreasMadsen
Copy link
Member

AndreasMadsen commented Jul 20, 2017

We are expecting something to be there and it is not. Forcing the community to track down crashes like that is not what I define as stable or safe to use. async_hooks are experimental, but this is breaking code that are not using them.

I agree. I suggested to @trevnorris that we skipped the entire assert in PushAsyncIds/PopAsyncIds when async_hooks isn't enabled. But he said that the assert prevented him from losing his sanity (#13548 (comment)).

I can write that test and default the value of that symbol to null there if it's the case, but I think this might be the cause of a lot of other breakages.

Sounds reasonable.


As far as I have seen the only two sources of error we have seen after v8.2.0 are from _writeRaw and _write in _http_outgoing. I think once we fix that, the number of issues should be reduced dramatically.

@mcollina
Copy link
Member Author

mcollina commented Jul 20, 2017

I'll work on the other PR to null things in _http_outgoing.

@@ -281,7 +281,7 @@ function setupNextTick() {
if (process._exiting)
return;

if (triggerAsyncId === null) {
if (triggerAsyncId === null || triggerAsyncId === undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I belive !Number.isSafeInteger(triggerAsyncId ) || triggerAsyncId < 0 is the "preferred" test

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that is actually a valid code path

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If not, double equals would be preferred to combine both checks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be simplified to just if (triggerAsyncId == null) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heh... sorry, just spotted @mscdex's identical comment ;-)

@mcollina
Copy link
Member Author

@trevnorris what do you think of this?

@trevnorris
Copy link
Contributor

trevnorris commented Jul 20, 2017

Change the statement to if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId <= 0). This will fix the current situation while also accommodating solution (2) from #14389 (comment).

@mcollina
Copy link
Member Author

@trevnorris if you prefer this over #14389, I'll patch it right now.

@trevnorris
Copy link
Contributor

@mcollina I prefer this, and am okay landing it any time. It's a change that my planned PR would need to make anyway.

@jasnell is this the only patch that we're looking at for the v8.2.1 release? if so then nm waiting on the release.

@jasnell
Copy link
Member

jasnell commented Jul 20, 2017

@trevnorris ... I'm still trying to determine if this is the only one :-) need more coffee. If @mcollina is happy with it and it addresses the immediate need, then +1

@mcollina
Copy link
Member Author

I'm happy. I'll add a test also for #14368, so we don't regress on that as well.

@mcollina
Copy link
Member Author

@trevnorris @addaleax @jasnell please review.

I've included also a fix for: #14368.
It's a separate commit, so we can easily remove/revert if it is not wanted.

@mcollina
Copy link
Member Author

const res = new Response();
const ws = new Writable({
write: common.mustCall((chunk, encoding, callback) => {
assert(chunk.toString().match(/hello world/));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this and not assert.strictEqual(chunk.toString(), 'hello world')?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the chunk contains a full HTTP response, which for brevity I did not want to include. common.mustCall it's also sufficient to validate the test on its own.

}

server.listen(0, common.mustCall(() => {
http.get('http://localhost:' + server.address().port)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit...

http.get(`http://localhost:${server.address().port}`);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

@jasnell jasnell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of nits but LGTM

@mcollina
Copy link
Member Author

@mcollina
Copy link
Member Author

@Fishrock123 Fishrock123 mentioned this pull request Jul 20, 2017
Copy link
Contributor

@trevnorris trevnorris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the changes

@Fishrock123
Copy link
Contributor

Thanks! landed in 107db33 and 69fdb47

Fishrock123 pushed a commit that referenced this pull request Jul 20, 2017
Fixes: #14386
Fixes: #14381

PR-URL: #14387
Reviewed-By: Refael Ackermann <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Trevor Norris <[email protected]>
Fishrock123 pushed a commit that referenced this pull request Jul 20, 2017
Fixes: #14368

PR-URL: #14387
Reviewed-By: Refael Ackermann <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Trevor Norris <[email protected]>
@Fishrock123
Copy link
Contributor

Uh oh, these work just fine on master but cause this error on v8.x-staging:

=== release test-tlswrap ===
Path: async-hooks/test-tlswrap
assert.js:60
  throw new errors.AssertionError({
  ^

AssertionError [ERR_ASSERTION]: Checking invocations at stage "client: when client destroyed":
     Called "before" 2 time(s), but expected 3 invocation(s).
    at checkHook (/Users/Jeremiah/Documents/node/test/async-hooks/hook-checks.js:51:14)
    at Array.forEach (native)
    at checkInvocations (/Users/Jeremiah/Documents/node/test/async-hooks/hook-checks.js:28:44)
    at tick1 (/Users/Jeremiah/Documents/node/test/async-hooks/test-tlswrap.js:93:5)
    at Immediate.ontick (/Users/Jeremiah/Documents/node/test/async-hooks/tick.js:7:37)
    at runCallback (timers.js:781:20)
    at tryOnImmediate (timers.js:743:5)
    at processImmediate [as _immediateCallback] (timers.js:714:5)
Command: out/Release/node /Users/Jeremiah/Documents/node/test/async-hooks/test-tlswrap.js

@Fishrock123
Copy link
Contributor

Fishrock123 commented Jul 20, 2017

Hmmmm, that might be flakey. I can't repo it now.

Fishrock123 pushed a commit that referenced this pull request Jul 20, 2017
Fixes: #14386
Fixes: #14381

PR-URL: #14387
Reviewed-By: Refael Ackermann <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Trevor Norris <[email protected]>
Fishrock123 pushed a commit that referenced this pull request Jul 20, 2017
Fixes: #14368

PR-URL: #14387
Reviewed-By: Refael Ackermann <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Trevor Norris <[email protected]>
@mcollina mcollina deleted the fix-14386 branch July 20, 2017 20:46
@refack
Copy link
Contributor

refack commented Jul 20, 2017

@Trott
Copy link
Member

Trott commented Jul 20, 2017

I stress tested against master locally. It's flaky:

$ tools/test.py -j92 --repeat 920 test/async-hooks/test-tlswrap.js 
=== release test-tlswrap ===                    
Path: async-hooks/test-tlswrap
assert.js:43
  throw new errors.AssertionError({
  ^

AssertionError [ERR_ASSERTION]: Checking invocations at stage "client: when client destroyed":
     Called "before" 2 time(s), but expected 3 invocation(s).
    at checkHook (/Users/trott/io.js/test/async-hooks/hook-checks.js:51:14)
    at Array.forEach (<anonymous>)
    at checkInvocations (/Users/trott/io.js/test/async-hooks/hook-checks.js:28:44)
    at tick1 (/Users/trott/io.js/test/async-hooks/test-tlswrap.js:93:5)
    at Immediate.ontick (/Users/trott/io.js/test/async-hooks/tick.js:7:37)
    at runCallback (timers.js:781:20)
    at tryOnImmediate (timers.js:743:5)
    at processImmediate [as _immediateCallback] (timers.js:714:5)
Command: out/Release/node /Users/trott/io.js/test/async-hooks/test-tlswrap.js

Probably race condition. The comment around before line 93 (where the test fails) kind of suggests that too:

    // TODO: why is client not destroyed here even after 5 ticks?
    // or could it be that it isn't actually destroyed until
    // the server is closed?

addaleax pushed a commit that referenced this pull request Jul 22, 2017
Fixes: #14386
Fixes: #14381

PR-URL: #14387
Reviewed-By: Refael Ackermann <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Trevor Norris <[email protected]>
addaleax pushed a commit that referenced this pull request Jul 22, 2017
Fixes: #14368

PR-URL: #14387
Reviewed-By: Refael Ackermann <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Trevor Norris <[email protected]>
@MylesBorins MylesBorins added the baking-for-lts PRs that need to wait before landing in a LTS release. label Aug 16, 2017
MylesBorins pushed a commit that referenced this pull request Oct 23, 2017
PR-URL: #15454
Ref: #14387
Ref: #14722
Ref: #14717
Ref: #15448
Reviewed-By: Refael Ackermann <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
addaleax pushed a commit to ayojs/ayo that referenced this pull request Oct 26, 2017
PR-URL: nodejs/node#15454
Ref: nodejs/node#14387
Ref: nodejs/node#14722
Ref: nodejs/node#14717
Ref: nodejs/node#15448
Reviewed-By: Refael Ackermann <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
addaleax pushed a commit to ayojs/ayo that referenced this pull request Dec 7, 2017
PR-URL: nodejs/node#15454
Ref: nodejs/node#14387
Ref: nodejs/node#14722
Ref: nodejs/node#14717
Ref: nodejs/node#15448
Reviewed-By: Refael Ackermann <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
@MylesBorins MylesBorins removed the baking-for-lts PRs that need to wait before landing in a LTS release. label Aug 17, 2018
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. process Issues and PRs related to the process subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.