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

feat: expose setPromiseHook support #8209

Closed
wants to merge 1 commit into from

Conversation

benjamingr
Copy link
Contributor

@benjamingr benjamingr commented Oct 31, 2020

This is a draft PR to expose promise hooks enabling APMs.

My rust is bad, I haven't used it in ±2 years, any criticism and feedback about the code below is very appreciated.

Still missing and need to do:

  • Document Deno.core.setPromiseHook
  • Refactor to use js_recv_obj to avoid the global (currently exposed).
  • Expose the constants used by the promise hooks (INIT, RESOLVE, BEFORE, AFTER)
  • Add tests (copy the Node or V8 promise tests probably?):
    • Test thenables
    • Test an async function
    • Test a "raw" promise
    • Test a promise subclass
  • Get APM vendor feedback in order to ensure this API "looks good".
  • Run formatter and linter

Nice to have in the future but out of scope:

  • Shim async_hooks using this in deno_std
  • Guide on how async hooks work

@benjamingr
Copy link
Contributor Author

@bartlomieju I've changed the code to use the same machinery as js_recv_cb (I hope) and it works. Would you mind taking a look and making sure I understood it correctly? (to be clear: none of this is urgent)

@benjamingr
Copy link
Contributor Author

Question: I noticed I can't find references to other Deno.core stuff in the docs - where (at all?) should this be documented?

@bartlomieju
Copy link
Member

@bartlomieju I've changed the code to use the same machinery as js_recv_cb (I hope) and it works. Would you mind taking a look and making sure I understood it correctly? (to be clear: none of this is urgent)

@benjamingr yeah, looks great!

@bartlomieju
Copy link
Member

Question: I noticed I can't find references to other Deno.core stuff in the docs - where (at all?) should this be documented?

Deno.core does not have type declarations at the moment - this is on purpose as we consider Deno.core unstable and that it can change at any moment. Let's ponder at this a bit more and decide what to do next when this PR is ready to land.

@benjamingr
Copy link
Contributor Author

After a comment by @Flarna I am convinced the correct behaviour here is to allow multiple consumers - so I will replace the Option to a Vec and add a test for multiple consumers.

@benjamingr
Copy link
Contributor Author

Also, on second thought I don't think it makes sense to unit test the behaviour in various cases I mentioned - but rather just our API surface (that is, test multiple consumers but not the exact promise-hook behaviour of promise subclasses for example) since it's tested on the V8 side.

I would like to (eventually) see an integration test (with a complex promise context tracking use case) added but I think the tests I've added probably cover the basic cases.

@benjamingr benjamingr marked this pull request as ready for review November 1, 2020 12:49
@benjamingr
Copy link
Contributor Author

Ok, this is about ready for review now @bartlomieju thanks for the help!

I feel strongly that this should be documented somewhere though, even if it just points to the V8 docs. I will happily write docs for it if you tell me where to put them :]

@kitsonk
Copy link
Contributor

kitsonk commented Nov 2, 2020

@benjamingr @bartlomieju my opinion on the documentation is that we should move embedding_deno to a new Advanced section in https://github.com/denoland/deno/tree/master/docs and add another document there about "Performance Monitoring" with this being the initial content.

Copy link
Contributor

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

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

LGTM insofar I'm able to judge deno_core changes.

There's no mechanism to remove a promise hook again. Personally, I'm fine with leaving that out for now but I thought I'd mention it anyway.

Comment on lines +206 to +207
for (i, hook_name) in
["INIT", "RESOLVE", "BEFORE", "AFTER"].iter().enumerate()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
for (i, hook_name) in
["INIT", "RESOLVE", "BEFORE", "AFTER"].iter().enumerate()
use v8::PromiseHookType as Ty;
for (hook_name, value) in
[("INIT", Ty::Init), ("RESOLVE", Ty::Resolve), ("BEFORE", Ty::Before), ("AFTER", Ty::After)].iter()

(and maybe sort them)

If the original name is good enough you can also do:

for v in [Ty::After, Ty::Before, Ty::Init, Ty::Resolve].iter() {
  let name = format!("{:?}", v);
  // ...
}

Comment on lines +796 to +799
let msg = v8::String::new(scope, "Invalid argument").unwrap();
let exception = v8::Exception::type_error(scope, msg);
scope.throw_exception(exception);
return;
Copy link
Contributor

Choose a reason for hiding this comment

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

For a future PR: consider moving this into a helper function. I count 5 or 6 other instances of the exact same pattern.

for js_promise_hook_cb_handle in state_rc.borrow().js_promise_hook_cb.iter() {
js_promise_hook_cb_handle
.get(scope)
.call(scope, global.into(), &[hook_type, promise.into(), parent])
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe use v8::undefined().into() instead of global.into()? Passing global is not wrong and it seems to be the predominant pattern in deno_core but it's marginally less efficient.

js_promise_hook_cb_handle
.get(scope)
.call(scope, global.into(), &[hook_type, promise.into(), parent])
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

You should probably not unwrap here unless you want exceptions from the promise hook to terminate the process (with a fairly opaque error message.)

@stale
Copy link

stale bot commented Feb 2, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Feb 2, 2021
@brillout
Copy link

brillout commented Feb 2, 2021

Any news on this? (I'm interested in porting https://github.com/reframejs/wildcard-api to Deno but for that I need async hooks.)

@stale stale bot removed the stale label Feb 2, 2021
@benjamingr
Copy link
Contributor Author

I am unfortunately in the process of switching jobs and moving houses and working on kids so I won't be able to work on this soon - other people are welcome to pick this up in the meantime.

@brillout
Copy link

brillout commented Feb 2, 2021

Ok I see, thanks for the update.

@kitsonk kitsonk added the feat new feature (which has been agreed to/accepted) label Feb 2, 2021
Base automatically changed from master to main February 19, 2021 14:58
@bartlomieju bartlomieju self-assigned this Jul 6, 2021
@bartlomieju
Copy link
Member

@bnoordhuis @benjamingr maybe we should wait for the SetPromiseHooks(init, before, after, resolve) API? (referenced in https://docs.google.com/document/d/1vtgoT4_kjgOr-Bl605HR2T6_SC-C8uWzYaOPDK5pmRo and nodejs/node#36394)

@benjamingr
Copy link
Contributor Author

It most likely should

@bartlomieju
Copy link
Member

It most likely should

Great, I'll close this PR for now, as it became stale. Once SetPromiseHooks() lands in V8 we can use this PR as a reference point for the implementation. Thank you @benjamingr!

@benjamingr
Copy link
Contributor Author

That sounds correct.

@bartlomieju
Copy link
Member

Yes, I believe it is

@bartlomieju
Copy link
Member

@benjamingr are you still interested in this functionality? I think we could try to tackle this once more with the API linked above.

@benjamingr
Copy link
Contributor Author

@bartlomieju I think Deno isn't actually production-ready until this API exists because it means it is impossible to write performance monitoring and analysis tools - it means users don't actually know where their production apps are spending time reliably which is a must for big real-world apps in my experience.

So yes - since I would like to see Deno used for production apps I am interested in this :) I've had Deno apps running "in production" since 2018 but I haven't been able to use it for anything substantial because of the lack of monitoring capabilities.

That said, I don't have time to work on this and I think your best approach to get this ready quickly would be to talk to APM vendors like New Relic or Elasticsearch and ask them to implement this. It's probably a good idea to have a discussion with APM vendors since they're the ones likely to eventually use this API the most.

Alternatively Deno can choose not to expose this functionality to end-users directly and provide its own APM offering which is also fine probably.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat new feature (which has been agreed to/accepted)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants