-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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: expose list of types #13610
Conversation
WIP (needs docs, and tests) |
lib/async_hooks.js
Outdated
function _getTypeName(module, type) { | ||
return Object.keys(JSSideProviders) | ||
.find((k) => JSSideProviders[k] === `${module} ${type}`); | ||
} |
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 seems unnecessarily complicated.
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 definatly a kludge.
This is what I came up with in my current (sleep deprived) state 😧
I'm looking for a structure that:
- Can get the
keys
out easily - can be queried for a specific name by modules and type.
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’d suggest really just listing the types for now
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 have a better idea: lookup by function:
_getTypeName(setTimeout)
, _getTypeName(setImmediate)
that way it's also useful to end users.
The question is will it be robust (no name conflicts)
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 question is will it be robust (no name conflicts)
That depends on what exactly your idea is – just have this for timers and nextTick? That doesn’t seem much more useful than listing these as values. If you’re trying to do this for generic async providers, you’ll run into the problem that many public APIs (e.g. net.Socket
) can init different async types.
that way it's also useful to end users.
I wouldn’t underscore-prefix it in that 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.
I have a better idea: lookup by function:
As @addaleax said, if you want this to work generically only a path of pain and suffering awaits you. I spent a couple weeks trying to get this and a similar feature to work. Will never work reliably.
TBH I'm not loving adding the module name to each type. Feels like serious overkill to me.
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 have a better idea: lookup by function:
As @addaleax said, if you want this to work generically only a path of pain and suffering awaits you. I spent a couple weeks trying to get this and a similar feature to work. Will never work reliably.TBH I'm not loving adding the module name to each type. Feels like serious overkill to me.
I didn't have any bright ideas. I think that I'd invert the dependency:
function registerTypeName(type) {
}
This way implementers (or node modules) could inform us of the names they choose. We could warn about possible coalitions, but that is obviously timing dependant.
lib/async_hooks.js
Outdated
@@ -480,4 +496,6 @@ module.exports = { | |||
emitBefore: emitBeforeS, | |||
emitAfter: emitAfterS, | |||
emitDestroy: emitDestroyS, | |||
// internal | |||
_getTypeName, |
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 this is meant for internal only, I would prefer that it be named with a Symbol rather than introducing a new _
-prefixed property. Or, better yet, move it to an appropriate lib/internal
location.
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.
made it public
This feels too complicated. Types were always meant to be a single string. Let's not mess with that. If we absolutely want to include the module name then just add it in the string. e.g. Though this PR gives me another idea. I like |
Holy hell, did you read my mind as I was typing out my last comment? |
Tbh, just export |
IMHO current imp is nice and compact.
Could go K.I.S.S. if current doesn't make sense... |
The copy and paste of |
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.
Yea, this is much better.
lib/timers.js
Outdated
@@ -190,7 +192,8 @@ function insert(item, unrefed) { | |||
item[async_id_symbol] = ++async_uid_fields[kAsyncUidCntr]; | |||
item[trigger_id_symbol] = initTriggerId(); | |||
if (async_hook_fields[kInit] > 0) | |||
emitInit(item[async_id_symbol], 'Timeout', item[trigger_id_symbol], item); | |||
emitInit(item[async_id_symbol], asyncTypeTO, item[trigger_id_symbol], |
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.
These changes are still confusing, imho. In any case, if you want to keep them, name them kTimeoutType
or similar since they are constants. :)
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
* refactor JS type names allocation
lib/async_hooks.js
Outdated
|
||
function registerTypeName(type) { | ||
if (JSProviders.has(type)) | ||
throw new errors.TypeError('ERR_ASYNC_PROVIDER_NAME', type); |
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.
here's some insanity, but could be useful for debugging:
const JSProviders = new Map();
function registerTypeName(type) {
if (JSProviders.has(type))
throw new errors.TypeError('ERR_ASYNC_PROVIDER_NAME', type); // include the old stack somehow
JSProviders.set(type, new Error().stack);
}
Not suggesting we implement this, but thought something like it could be helpful for identifying the bugger who is using my type name.
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.
could do:
const JSProviders = new Map();
function registerTypeName(type) {
if (JSProviders.has(type))
throw JSProviders.get(type);
JSProviders.set(type, new errors.TypeError('ERR_ASYNC_PROVIDER_NAME', type));
}
Will throw weird errors, good for the AsyncResource implementer, bad for a 6-degrees-of-separation user
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 worry about the overhead of getTypes()
(I've used it before in init()
), but will worry about improving that later. Just don't forget to name the constants for timers and nexttick. Thanks!
added: REPLACEME | ||
--> | ||
|
||
* Returns: `{Array}` list of async resource type names |
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 "list of async resource type names" is redundant considering it is documented two lines below.
added: REPLACEME | ||
--> | ||
|
||
* Returns: `{Array}` list of async resource type names |
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 "list of async resource type names" is redundant considering it is documented two lines below.
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.
Some note about why these are useful would be helpful here.
@@ -428,6 +438,13 @@ const server = net.createServer(function onConnection(conn) { | |||
}); | |||
``` | |||
|
|||
#### `async_hooks.registerTypeName(type)` |
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.
added YAML annotation needed.
#### `async_hooks.registerTypeName(type)` | ||
|
||
* `type` {string} a new type of async resource | ||
* Returns {string} the type name to use |
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.
Why not just return undefined
? The way it is documented can be interpreted to imply some processing has been done on type
, while it is actually returned verbatim.
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 find it convenient to do the same as is also already in this PR:
const kTickObjectType = async_hooks.registerTypeName('TickObject');
@@ -461,6 +462,18 @@ function init(asyncId, type, triggerId, resource) { | |||
processing_hook = false; | |||
} | |||
|
|||
const JSProviders = new Set(); | |||
function registerTypeName(type) { | |||
if (JSProviders.has(type)) |
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.
Maybe we should make sure type
isn't already contained in async_wrap.Providers
either?
|
||
function getTypes() { | ||
return Object.keys(async_wrap.Providers) | ||
.concat(...JSProviders.keys()); |
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.
No need for .keys()
. Set
's iteration protocol returns its contents by default.
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 is certainly not the fastest method, but given that this shouldn't be a hot code path that should be ok
@@ -131,6 +131,16 @@ provided by AsyncHooks itself. The logging should then be skipped when | |||
it was the logging itself that caused AsyncHooks callback to call. By | |||
doing this the otherwise infinite recursion is broken. | |||
|
|||
#### `async_hooks.getTypes()` |
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.
Alphabetical order.
@@ -461,6 +462,18 @@ function init(asyncId, type, triggerId, resource) { | |||
processing_hook = false; | |||
} | |||
|
|||
const JSProviders = new Set(); | |||
function registerTypeName(type) { |
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'd like to see a type check here, ensuring type
is a string.
@@ -131,6 +131,16 @@ provided by AsyncHooks itself. The logging should then be skipped when | |||
it was the logging itself that caused AsyncHooks callback to call. By | |||
doing this the otherwise infinite recursion is broken. | |||
|
|||
#### `async_hooks.getTypes()` | |||
|
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.
Unnecessary whitespace before the yaml comment
|
@AndreasMadsen I like the idea of being able to register a type name and having it error if the name has already been registered. Though I also think the file name where the name was registered should be in the error message to help developers identify where it's happening (can see myself freaking out not being able to identify which dependency is using a specific name). I'm impartial to how the type names are accessed, but it does seem that adding values to |
Completely agree. I'm just saying that it has little value if the user can just pass whatever value they want in |
@refack this needs a rebase, the comments should be addressed and I am not sure if this is in a state that would land as is. Would you mind following up on this? |
Ping @refack |
Closing due to long inactivity. @refack if you want to pursue this further, please feel free to reopen! |
Ref: #13561 (comment)
Also refactor JS type names allocation, so it provided by
asunc_hooks
/cc @nodejs/async_hooks
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
async_hooks,timers,process