diff --git a/packages/opentelemetry-context-async-hooks/src/AsyncHooksContextManager.ts b/packages/opentelemetry-context-async-hooks/src/AsyncHooksContextManager.ts index 1e32db350..05b9fd987 100644 --- a/packages/opentelemetry-context-async-hooks/src/AsyncHooksContextManager.ts +++ b/packages/opentelemetry-context-async-hooks/src/AsyncHooksContextManager.ts @@ -66,8 +66,15 @@ export class AsyncHooksContextManager extends AbstractAsyncHooksContextManager { * Init hook will be called when userland create a async context, setting the * context as the current one if it exist. * @param uid id of the async context + * @param type the resource type */ - private _init(uid: number) { + private _init(uid: number, type: string) { + // ignore TIMERWRAP as they combine timers with same timeout which can lead to + // false context propagation. TIMERWRAP has been removed in node 11 + // every timer has it's own `Timeout` resource anyway which is used to propagete + // context. + if (type === 'TIMERWRAP') return; + const context = this._stack[this._stack.length - 1]; if (context !== undefined) { this._contexts.set(uid, context); diff --git a/packages/opentelemetry-context-async-hooks/test/AsyncHooksContextManager.test.ts b/packages/opentelemetry-context-async-hooks/test/AsyncHooksContextManager.test.ts index c8ab82ca1..6a2fbf050 100644 --- a/packages/opentelemetry-context-async-hooks/test/AsyncHooksContextManager.test.ts +++ b/packages/opentelemetry-context-async-hooks/test/AsyncHooksContextManager.test.ts @@ -222,6 +222,34 @@ for (const contextManagerClass of [ }); assert.strictEqual(contextManager.active(), Context.ROOT_CONTEXT); }); + + it('should work with timers using the same timeout', done => { + let cnt = 3; + function countDown() { + cnt--; + if (cnt === 0) done(); + if (cnt < 0) throw new Error('too many calls to countDown()'); + } + + const time1 = 2; + const time2 = time1 + 1; + const rootCtx = contextManager.active(); + const innerCtx = rootCtx.setValue(Symbol('test'), 23); + contextManager.with(innerCtx, () => { + setTimeout(() => { + assert.strictEqual(contextManager.active(), innerCtx); + countDown(); + }, time1); + }); + setTimeout(() => { + assert.strictEqual(contextManager.active(), rootCtx); + countDown(); + }, time1); + setTimeout(() => { + assert.strictEqual(contextManager.active(), rootCtx); + countDown(); + }, time2); + }); }); describe('.bind(function)', () => {