From 3bc4d57977b29d1972178a8f25218c77014f56a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerhard=20St=C3=B6bich?= Date: Mon, 21 Sep 2020 18:18:07 +0200 Subject: [PATCH] fix: ignore TIMERWRAP in AsyncHooksContextManager (#1530) Co-authored-by: Daniel Dyla --- .../src/AsyncHooksContextManager.ts | 9 +++++- .../test/AsyncHooksContextManager.test.ts | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry-context-async-hooks/src/AsyncHooksContextManager.ts b/packages/opentelemetry-context-async-hooks/src/AsyncHooksContextManager.ts index 1e32db3502..05b9fd9875 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 c8ab82ca12..6a2fbf0507 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)', () => {