-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
domain: re-implement domain over async_hook #16222
Conversation
lib/domain.js
Outdated
|
||
function Domain() { | ||
EventEmitter.call(this); | ||
if (process.domain === self) { // if this operation is created while in a domain, let's mark it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably also want to set resource.domain = process.domain
in this case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, good point!
Cool that you’re taking this on! I’ll assign myself because I really want to see this happen and hope I can help here, please don’t take this as a sign that I’ll be claiming any specific tasks for only me :) |
Thanks @addaleax . Let's see how we can save domains! |
lib/domain.js
Outdated
|
||
Domain.prototype._enable = function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you make this function a top-level function? Exposing it inside the prototype does not seem correct, as an external user could call it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
lib/domain.js
Outdated
this.members = []; | ||
this.asyncIds = new Set(); | ||
this.asyncHook = null; // will hold the asyncHook after _enable is called | ||
this._enable(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will make every domain created register new async_hooks. I'm not 100% sure this is the intended usage, and what side effect it would create. cc @AndreasMadsen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think a Map
that keeps track of the async id
→ domain
mapping would be a better idea…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
lib/domain.js
Outdated
constructor(...args) { | ||
super(...args); | ||
|
||
this.domain = process.domain; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't work for async function
promises. Set the property using resource.promise
in the init-hook instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Be careful with #15673 if you do that though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great pointer, thanks @AndreasMadsen
@TimothyGu is there any way to know if the promise has been init in such context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vdeturckheim I think resource.promise instanceof Promise
should be fine, it’s false
for promises from other contexts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it works, thanks @addaleax !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to review this in detail but I don't have time right now. The comments made so far are worth addressing anyway.
Thanks for doing this.
PS: also remember to clean up in C++ land too.
lib/domain.js
Outdated
after(asyncId) { | ||
|
||
if (pairing.has(asyncId)) { // exit domain for this cb | ||
process.domain = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it not be possible to exit one domain and enter another, as in nested domains?
This kind of before
and after
pattern usually requires a stack, so I'm always suspicious when no stack is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack. Would make sense yes!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I should even use domain.enter
and domain.exit
for before
and after
hooks. That would use the domain stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely!
lib/domain.js
Outdated
@@ -48,13 +48,61 @@ Object.defineProperty(process, 'domain', { | |||
} | |||
}); | |||
|
|||
let asyncHook = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just call createHook
immediately, it is simpler. We do that for the the inspector
integration and soon trace_events
integration too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point!
lib/domain.js
Outdated
|
||
if (process.domain !== null) { // if this operation is created while in a domain, let's mark it | ||
pairing.set(asyncId, process.domain); | ||
resource.domain = process.domain; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was the handle objects mutated before too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't thinks so, it was made on request #16222 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@addaleax could you explain why this is needed? I see no need to add extra domain
complexity if it didn't already exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AndreasMadsen The thing is – it does already exist. With this, we can remove the code that sets it in other places, which would be very nice (look around for domain_string()
in the source).
For event emitters (and therefore the handle wraps), the property is added in events.js
, so we might need to come up with a better solution there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, by the way; this is essentially how MakeCallback
kept track of the domain – by looking at the resource object’s .domain
property
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing is – it does already exist.
Okay, then it is fine. I thought it was only EventEmitter
and Promise
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, those + all request types – that sums up everything in core …
It would be nice if we could remove https://github.com/nodejs/node/blob/master/src/node.cc#L1105L1278 at the very least |
Yeah. It has always been a mystery to me, how |
@AndreasMadsen agreed regarding c++ land. I am currently looking on how to have DomainsStackHasErrorHandler (https://github.com/nodejs/node/blob/master/src/node.cc#L1127) working with this. |
lib/domain.js
Outdated
pairing.delete(asyncId); // cleaning up | ||
} | ||
}); | ||
asyncHook.enable(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this to the Domain
constructor, so require('domain')
in itself doesn't slow us down. If the asyncHook
is already enabled it will just be a noop. Hooks can only be enabled once.
src/node.cc
Outdated
|
||
process_object->Set(env->tick_callback_string(), tick_callback_function); | ||
env->set_tick_callback_function(tick_callback_function); | ||
|
||
CHECK(args[0]->IsArray()); | ||
env->set_domain_array(args[0].As<Array>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be able to remove this too, including all uses of domain_array
. As I see itset_domain_array
is only used to set the domain
property on resource object in C++. We now do that using async_hooks
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack! good catch!
I looked at |
@AndreasMadsen thanks for the pointers here. Seems to work now \o/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m pretty sure there are more things we can do but this is a really good first step.
// It's possible to enter one domain while already inside | ||
// another one. The stack is each entered domain. | ||
const stack = []; | ||
exports._stack = stack; | ||
process._setupDomainUse(stack); | ||
|
||
class Domain extends EventEmitter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically changing this to ES6 style classes is API breaking. We are likely going to land this in node 9 anyway so it shouldn't be a problem. Just as a note.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After a closer look, I think we can remove a little more.
|
||
// Do a little housekeeping. | ||
env->process_object()->Delete( | ||
env->context(), | ||
FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupDomainUse")).FromJust(); | ||
|
||
uint32_t* const fields = env->domain_flag()->fields(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the domain_flag
declaration from env.h
and env-inl.h
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be good now
CHECK(args[0]->IsArray()); | ||
env->set_domain_array(args[0].As<Array>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was lost in a collapsed comment: we can remove all uses of domain_array
. Just do a search for domain_array
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be good now. I cleaned up some domain related flags in multiple places ( 821f51d ) Hop I did not miss any
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it compiles without the domain_array
definition I think it should be good :)
uint32_t* const fields = env->domain_flag()->fields(); | ||
uint32_t const fields_count = env->domain_flag()->fields_count(); | ||
|
||
Local<ArrayBuffer> array_buffer = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't figure out what this array_buffer
was actually used for before :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not certain either. Seems to come from this PR #2022 . But could not really see in detail what it does.
@@ -1155,7 +1154,6 @@ bool ShouldAbortOnUncaughtException(Isolate* isolate) { | |||
return isEmittingTopLevelDomainError || !DomainsStackHasErrorHandler(env); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to understand why the returned caught
boolean in process._fatalException
is not enough to determine if we should abort on uncaughtException
.
@@ -563,7 +561,6 @@ class Environment { | |||
inline void FinishHandleCleanup(uv_handle_t* handle); | |||
|
|||
inline AsyncHooks* async_hooks(); | |||
inline DomainFlag* domain_flag(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The DomainFlag
type is no longer used. Can you remove the class
Line 466 in e3503ac
class DomainFlag { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM again :)
I'm sure we can do more. But it doesn't have to be in one PR. I think it would be really nice to get the exception handling out of C++. I don't think we can do so much about the DomainBefore
and DomainAfter
parts, without breaking backward compatibility to some extent. But we should definitely consider doing too, it is not like we make it impossible to use domain
with native addons, they can just use async_hooks
:)
Awesome thanks! Btw, do we want to update the depreciation status of the API in this PR too? |
Due to some changes to async tracking of http and also in how domains are handled, it's no longer necessary to manually copy domain from req to res in http code. PR-URL: #18477 Refs: #16222 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Khaidi Chu <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
It is no longer necessary to explicitly set the handle to inherit the Timeout domain. PR-URL: #18477 Refs: #16222 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Khaidi Chu <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
Due to some changes to async tracking of http and also in how domains are handled, it's no longer necessary to manually copy domain from req to res in http code. PR-URL: #18477 Refs: #16222 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Khaidi Chu <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
It is no longer necessary to explicitly set the handle to inherit the Timeout domain. PR-URL: #18477 Refs: #16222 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Khaidi Chu <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
Due to some changes to async tracking of http and also in how domains are handled, it's no longer necessary to manually copy domain from req to res in http code. PR-URL: #18477 Refs: #16222 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Khaidi Chu <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
It is no longer necessary to explicitly set the handle to inherit the Timeout domain. PR-URL: #18477 Refs: #16222 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Khaidi Chu <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
Due to some changes to async tracking of http and also in how domains are handled, it's no longer necessary to manually copy domain from req to res in http code. PR-URL: nodejs#18477 Refs: nodejs#16222 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Khaidi Chu <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
It is no longer necessary to explicitly set the handle to inherit the Timeout domain. PR-URL: nodejs#18477 Refs: nodejs#16222 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Khaidi Chu <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
test-microtask-queue-run-immediate-domain.js tests that the behavior of another test, test-microtask-queue-run-immediate.js, is still consistent when the core domain module is loaded. This was needed because before the changes in #1622 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and as such that test is no longer needed. R-URL: #23252 Refs: #16222 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: James M Snell <[email protected]>
test-microtask-queue-run-immediate-domain.js tests that the behavior of another test, test-microtask-queue-run-immediate.js, is still consistent when the core domain module is loaded. This was needed because before the changes in #1622 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and as such that test is no longer needed. R-URL: #23252 Refs: #16222 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: James M Snell <[email protected]>
test-microtask-queue-run-immediate-domain.js tests that the behavior of another test, test-microtask-queue-run-immediate.js, is still consistent when the core domain module is loaded. This was needed because before the changes in #1622 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and as such that test is no longer needed. R-URL: #23252 Refs: #16222 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: James M Snell <[email protected]>
test-microtask-queue-integration-domain.js tests that the behavior of another test, test-microtask-queue-integration.js, is still consistent when the core domain module is loaded. This was needed because before the changes in nodejs#16222 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and therefore that test is no longer needed.
test-microtask-queue-run-domain.js tests that the behavior of another test, test-microtask-queue-run.js, is still consistent when the core domain module is loaded. This was needed because before the changes in nodejs#16222 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and therefore that test is no longer needed.
test-microtask-queue-integration-domain.js tests that the behavior of another test, test-microtask-queue-integration.js, is still consistent when the core domain module is loaded. This was needed because before the changes in #16222 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and therefore that test is no longer needed. PR-URL: #23342 Reviewed-By: Vladimir de Turckheim <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
test-microtask-queue-run-domain.js tests that the behavior of another test, test-microtask-queue-run.js, is still consistent when the core domain module is loaded. This was needed because before the changes in #16222 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and therefore that test is no longer needed. PR-URL: #23343 Reviewed-By: Vladimir de Turckheim <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
test-microtask-queue-run-immediate-domain.js tests that the behavior of another test, test-microtask-queue-run-immediate.js, is still consistent when the core domain module is loaded. This was needed because before the changes in #1622 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and as such that test is no longer needed. R-URL: #23252 Refs: #16222 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: James M Snell <[email protected]>
test-microtask-queue-integration-domain.js tests that the behavior of another test, test-microtask-queue-integration.js, is still consistent when the core domain module is loaded. This was needed because before the changes in #16222 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and therefore that test is no longer needed. PR-URL: #23342 Reviewed-By: Vladimir de Turckheim <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
test-microtask-queue-run-domain.js tests that the behavior of another test, test-microtask-queue-run.js, is still consistent when the core domain module is loaded. This was needed because before the changes in #16222 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and therefore that test is no longer needed. PR-URL: #23343 Reviewed-By: Vladimir de Turckheim <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
test-microtask-queue-run-immediate-domain.js tests that the behavior of another test, test-microtask-queue-run-immediate.js, is still consistent when the core domain module is loaded. This was needed because before the changes in nodejs/node#1622 were merged, the core domain module would replace the function that would call nextTick callbacks with a different implementation. This is no longer the case, and as such that test is no longer needed. Refs: nodejs/node#16222
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
Tentative to re-implement domains over async_hook API.
Currently this breaks the following tests:
those using--abort-on-uncaught-exception
flag: I have not found a way to intercept those errors event by looking toprocess._fatalException
parallel/test-domain-abort-on-uncaught.jsparallel/test-domain-with-abort-on-uncaught-exceptionthe one which expect an exit code of 7 when an error is thrown from a domain error handler: I have not found where this was done in the previous version.test-domain-top-level-error-handler-throwthose linked tomakeCallback
: It seems there is some domain handling happening in native code I could not fix.addons/make-callback-recurse/testaddons-napi/test_make_callback_recurse/test