diff --git a/plugins/node/opentelemetry-instrumentation-redis-4/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-redis-4/src/instrumentation.ts index aa30a98822..3ac531847f 100644 --- a/plugins/node/opentelemetry-instrumentation-redis-4/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-redis-4/src/instrumentation.ts @@ -286,41 +286,61 @@ export class RedisInstrumentation extends InstrumentationBase { return execRes; } - execRes.then((redisRes: unknown[]) => { - const openSpans = this[OTEL_OPEN_SPANS]; - if (!openSpans) { - return plugin._diag.error( - 'cannot find open spans to end for redis multi command' - ); - } - if (redisRes.length !== openSpans.length) { - return plugin._diag.error( - 'number of multi command spans does not match response from redis' - ); - } - for (let i = 0; i < openSpans.length; i++) { - const { span, commandName, commandArgs } = openSpans[i]; - const currCommandRes = redisRes[i]; - if (currCommandRes instanceof Error) { - plugin._endSpanWithResponse( - span, - commandName, - commandArgs, - null, - currCommandRes + return execRes + .then((redisRes: unknown[]) => { + const openSpans = this[OTEL_OPEN_SPANS]; + if (!openSpans) { + return plugin._diag.error( + 'cannot find open spans to end for redis multi command' + ); + } + if (redisRes.length !== openSpans.length) { + return plugin._diag.error( + 'number of multi command spans does not match response from redis' ); - } else { + } + for (let i = 0; i < openSpans.length; i++) { + const { span, commandName, commandArgs } = openSpans[i]; + const currCommandRes = redisRes[i]; + if (currCommandRes instanceof Error) { + plugin._endSpanWithResponse( + span, + commandName, + commandArgs, + null, + currCommandRes + ); + } else { + plugin._endSpanWithResponse( + span, + commandName, + commandArgs, + currCommandRes, + undefined + ); + } + } + return redisRes; + }) + .catch((err: Error) => { + const openSpans = this[OTEL_OPEN_SPANS]; + if (!openSpans) { + return plugin._diag.error( + 'cannot find open spans to end for redis multi command' + ); + } + for (let i = 0; i < openSpans.length; i++) { + const { span, commandName, commandArgs } = openSpans[i]; plugin._endSpanWithResponse( span, commandName, commandArgs, - currCommandRes, - undefined + null, + err ); } - } - }); - return execRes; + return Promise.reject(err); + }); }; }; } diff --git a/plugins/node/opentelemetry-instrumentation-redis-4/test/redis.test.ts b/plugins/node/opentelemetry-instrumentation-redis-4/test/redis.test.ts index ac5abf5bce..74fa7b699c 100644 --- a/plugins/node/opentelemetry-instrumentation-redis-4/test/redis.test.ts +++ b/plugins/node/opentelemetry-instrumentation-redis-4/test/redis.test.ts @@ -32,7 +32,7 @@ const instrumentation = registerInstrumentationTesting( new RedisInstrumentation() ); -import { createClient } from 'redis'; +import { createClient, WatchError } from 'redis'; import { Span, SpanKind, @@ -398,6 +398,26 @@ describe('redis@^4.0.0', () => { ); }); + it('multi command that rejects', async () => { + const watchedKey = 'watched-key'; + await client.watch(watchedKey); + await client.set(watchedKey, 'a different value'); + try { + await client.multi().get(watchedKey).exec(); + assert.fail('expected WatchError to be thrown and caught in try/catch'); + } catch (error) { + assert.ok(error instanceof WatchError); + } + + // All the multi spans' status are set to ERROR. + const [_watchSpan, _setSpan, multiGetSpan] = getTestSpans(); + assert.strictEqual(multiGetSpan?.status.code, SpanStatusCode.ERROR); + assert.strictEqual( + multiGetSpan?.status.message, + 'One (or more) of the watched keys has been changed' + ); + }); + it('duration covers create until server response', async () => { await client.set('another-key', 'another-value'); const multiClient = client.multi();