When a user creates an AsyncHooksContextManager, it is easy to forget to call enable, which can cause issues when used with promises. We should consider enabling the context manager automatically, or at least warning if a context manager is used without a call to enable.
I have a simple use case which loses context when using AsyncHooksContextManager:
const api = require("@opentelemetry/api");
const tracing = require("@opentelemetry/tracing");
const contextAsyncHooks = require("@opentelemetry/context-async-hooks");
api.context.setGlobalContextManager(new contextAsyncHooks.AsyncHooksContextManager());
api.trace.setGlobalTracerProvider(new tracing.BasicTracerProvider());
const tracer = api.trace.getTracer("context-loss-test");
const span = tracer.startSpan("test");
tracer.withSpan(span, () => contextLossFunction())
async function contextLossFunction() {
let currentSpan = tracer.getCurrentSpan();
if (currentSpan) {
console.log("before await", currentSpan.context().traceId);
} else {
console.log("context lost")
}
await (async () => {})()
currentSpan = tracer.getCurrentSpan();
if (currentSpan) {
console.log("after await", currentSpan.context().traceId);
} else {
console.log("context lost")
}
}
output:
before await 12305e9b40d445d997f5da1e420bcc70
context lost
Seems very similar to #752 and #1101
I thought this case should have been solved by #1099
/cc @vmarchaud
@dyladan With which version you reproduced the issue ? (both node and ot)
Latest ot (0.10.2)
Multiple node versions including the latest of both 12 and 14.
The AsyncLocalStorageContextManager does not have this bug, but the version of node available in our target environment is running a version of node 12 that is not late enough to be able to use it.
I can't reproduce with the following test (node v14.7.0, master branch):
it('should not loose the context (async/await)', done => {
const scope1 = '1' as any;
contextManager.with(scope1, async () => {
assert.strictEqual(contextManager.active(), scope1);
await (async () => {})()
assert.strictEqual(contextManager.active(), scope1);
return done();
});
assert.strictEqual(contextManager.active(), Context.ROOT_CONTEXT);
});
And i believe this is tested with this test
EDIT: I didnt mention it but i reproduce with your exact code though
Following your example, another simple reproduction:
const { AsyncHooksContextManager } = require("@opentelemetry/context-async-hooks");
const contextManager = new AsyncHooksContextManager();
const scope1 = '1';
contextManager.with(scope1, async () => {
console.log(contextManager.active()) // 1
await (async () => { })()
console.log(contextManager.active()) // Context { _currentContext: Map(0) {} }
});
I don't know what is different about it in the test environment vs in the reproduction.
Fixed with one line:
const { AsyncHooksContextManager } = require("@opentelemetry/context-async-hooks");
const contextManager = new AsyncHooksContextManager();
contextManager.enable()
const scope1 = '1';
contextManager.with(scope1, async () => {
console.log(contextManager.active())
await (async () => { })()
console.log(contextManager.active())
});
I feel silly now
Ahah no worries i already spent few hours trying to debug issue when i wasn't enabling the it. Wondering if we could warn at some point that it is used without being enabled
Is there some reason we don't automatically enable?
@dyladan At the root i believe we wanted to choose whent to enable it (because the performance hit only is there after the enable), but i believe it don't make much sense with our current internal usage. Would be a breaking change though so we might want to take a decision before GA
I think if the user creates an async hooks context manager, the expectation that async hooks is enabled is a reasonable one.
Sounds related: https://github.com/open-telemetry/opentelemetry-js/issues/758
FWIW the AsyncLocalStorage in node uses autoenable - even after disable was called.
disable is present mostly to have some emergency tool in case something goes wrong. And for the rare case it's really not needed anymore to allow GC to collect it.
Frequently disable/enable of a context manager will likely cause problems than solving ones.
Maybe instead we should remove enable/disable from the interface and always auto-enable?
Most helpful comment
I think if the user creates an async hooks context manager, the expectation that async hooks is enabled is a reasonable one.