-
Notifications
You must be signed in to change notification settings - Fork 821
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
Unable to rely on AsyncHooksScopeManager when wrapping async functions #752
Comments
I believe it's because the callback can't be async, could you try to something like this :
|
@vmarchaud I do believe that would work but I'm writing a logging library and need to be able to give more flexibility than this offers. I need to be able to rely on the scope manager to manage the scope properly. I found that by modifying the scope manager as so: from: with(scope, fn) {
const uid = asyncHooks.executionAsyncId();
const oldScope = this._scopes[uid];
this._scopes[uid] = scope;
try {
return fn();
}
catch (err) {
throw err;
}
finally {
if (oldScope === undefined) {
this._destroy(uid);
}
else {
this._scopes[uid] = oldScope;
}
}
} to async with(scope, fn) {
const uid = asyncHooks.executionAsyncId();
const oldScope = this._scopes[uid];
this._scopes[uid] = scope;
try {
return await fn();
}
catch (err) {
throw err;
}
finally {
if (oldScope === undefined) {
this._destroy(uid);
}
else {
this._scopes[uid] = oldScope;
}
}
} |
Indeed that would works but i believe it could have some performance impact, might need to dig a little bit more to make that change |
could be viable to create a second alongside the original? |
@cohen990 Might be a solution indeed, don't know if we could detect if the original function is async or not. |
https://davidwalsh.name/javascript-detect-async-function
This apparently works? |
Yep, that could do the trick. Do you have the time to make a PR for that ? |
I do indeed :) |
Feel free to ping me on gitter if you need any help :) |
It won't work |
@cohen990 I'd be cautious about using the constructor trick to figure out whether the traced function is async. The problem is that functions can be async by virtue of returning a Promise. I think you might be setting yourself up for bugs later. Is it worth testing the perf impact of always awaiting? |
@bobthemighty I believe the library has a suite of performance tests. I'll dig in |
@bobthemighty It is and we have benchmark there, one would need to add a case with |
It seems like the performance impact would actually be pretty significant: multiple orders of magnitude. I am in favor of adding a const Benchmark = require('benchmark');
const pretty = require('beautify-benchmark');
new Benchmark.Suite()
.on('cycle', event => pretty.add(event.target))
.on('error', event => { throw event.target.error; })
.on('complete', function () { pretty.log(); })
.add('async', {
defer: true,
fn: async function (deferred) {
await withAsync(foo);
deferred.resolve();
}
})
.add('sync', {
defer: false,
fn: function () {
_with(foo);
}
})
.run()
function foo() {
return "bar";
}
function _with(fn) {
try {
return fn();
}
catch (err) {
throw err;
}
}
async function withAsync(fn) {
try {
return await fn();
}
catch (err) {
throw err;
}
}
|
Considering the impact i also think we should add a |
This would have to be propagated through to the Tracer level too. The tracers will need to offer a |
@cohen990 did you make the same test in web only with ZoneScopeManager without modifying the code yet ? Does it work ? |
would something like this work ?
|
I think there are two issues here:
Even if adding an |
@obecny I think the ideal solution would be one that is flexible enough to allow the library to be used in whatever way seems natural. I might abandon this separate |
What I miss also in above sample is a call to |
Hi thanks for all your help. I've decided in the end to not use the |
Instead of adding a new method, couldn't we test the return value of the function, and see if it looks like a promise? If so, we would await it, otherwise we keep it like it is now. The minor check should not impact performance that much for sync code. |
It's not as simple as just awaiting it unfortunately as new async contexts are created. @vmarchaud is looking into a solution with cls-hooked currently |
Yes unfortunately |
@satazor It has specific issue but depending on how you want to use it, it may have no impact. However the method is still not implemented on the tracer so its quite hard to use it for now |
Hi everyone, great project. I am struggling with tracing beyond an await call: tracer.getCurrentSpan().addEvent('starting await'); Running without await works perfectly fine, but adding it results in an undefined span: "Cannot read property 'addEvent' of undefined" Any assistance would be appreciated. |
What if you run it inside the scope of the desired span ?
|
Hey @obecny, thanks for replying! These are my versions: Do you perhaps know in which version withAsync was implemented? Also, FYI this is how I create my tracer: --> tracer.js // Create a provider for activating and tracking spans // Create an exporter for sending spans data // Configure a span processor for the tracer // Register the tracer const tracer = opentelemetry.trace.getTracer(); module.exports = { |
We only landed the |
@vmarchaud thx, so @Whoweez this is not yet possible |
hi @obecny, thanks! Although, "tracer.with" results in "tracer.with is not a function". Not too sure where this interface comes from in the above version I'm running. Is it perhaps in a another version? All the interface specifies is: |
@Whoweez sry I meant |
Thanks @obecny. With the above compiled tracer, I get the same error as I did when I ran it without the wrapper: "Cannot read property 'addEvent' of undefined" => on the second getCurrentSpan() tracer.withSpan(tracer.getCurrentSpan(), async () => { I've also tried adding the: But this too delivers the same error. On this issue, and the reason why I have jumped onto this thread: with express API running the above tracer:
The result is that with the first request all the traces are neatly compiled into a waterfall scene whereas with the second call each subsequent span definition, in all my methods, is split across separate traces. This is just to give you all some context as to why I'm bugging you here, perhaps my setup could be fixed in another way...? Let me know what you think. Chio, Wzz |
@Whoweez there are no solution available now, thats why the issue is still open |
@vmarchaud, thank you for the feedback. I'll wait for the implementation. Thanks again for the great repo! Take care :) |
Please answer these questions before submitting a bug report.
What version of OpenTelemetry are you using?
0.3.3
What version of Node are you using?
13.6.0
What did you do?
I'm using the AsyncHooksScopeManager to keep track of my current scope. I'm expecting to be able to call
tracer.withSpan(...)
and from within calltracer.getCurrentSpan()
and have it provide me with the span I am executing inside of. I've provided a failing unit testWhat did you see instead?
Instead of giving me the current span, the ScopeManager has continued executing (because it doesn't await) and has deleted the scope for that span. So calling
getCurrentSpan()
is returning undefinedThe text was updated successfully, but these errors were encountered: