Skip to content

Commit a89e597

Browse files
committed
fix(tracing): Handle cursors returned from Mongodb operations.
1 parent 591f9cb commit a89e597

File tree

3 files changed

+26
-15
lines changed

3 files changed

+26
-15
lines changed

packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/scenario.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ async function run(): Promise<void> {
3636
await collection.findOne({ title: 'Back to the Future' });
3737
await collection.updateOne({ title: 'Back to the Future' }, { $set: { title: 'South Park' } });
3838
await collection.findOne({ title: 'South Park' });
39+
40+
await collection.find({ title: 'South Park' }).toArray();
3941
} finally {
4042
if (transaction) transaction.finish();
4143
await client.close();

packages/node-integration-tests/suites/tracing/auto-instrument/mongodb/test.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,6 @@ conditionalTest({ min: 12 })('MongoDB Test', () => {
4848
description: 'findOne',
4949
op: 'db',
5050
},
51-
{
52-
data: {
53-
collectionName: 'movies',
54-
dbName: 'admin',
55-
namespace: 'admin.movies',
56-
query: '{"title":"Back to the Future"}',
57-
},
58-
description: 'find',
59-
op: 'db',
60-
},
6151
{
6252
data: {
6353
collectionName: 'movies',

packages/tracing/src/integrations/node/mongo.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Hub } from '@sentry/core';
22
import { EventProcessor, Integration, SpanContext } from '@sentry/types';
3-
import { fill, isThenable, loadModule, logger } from '@sentry/utils';
3+
import { fill, isInstanceOf, isThenable, loadModule, logger } from '@sentry/utils';
4+
import { EventEmitter } from 'events';
45

56
import { shouldDisableAutoInstrumentation } from './utils/node-utils';
67

@@ -160,20 +161,38 @@ export class Mongo implements Integration {
160161
// its (non-callback) arguments can also be functions.)
161162
if (typeof lastArg !== 'function' || (operation === 'mapReduce' && args.length === 2)) {
162163
const span = parentSpan?.startChild(getSpanContext(this, operation, args));
163-
const maybePromise = orig.call(this, ...args) as Promise<unknown>;
164+
const maybePromiseOrCursor = orig.call(this, ...args);
164165

165-
if (isThenable(maybePromise)) {
166-
return maybePromise.then((res: unknown) => {
166+
if (isThenable(maybePromiseOrCursor)) {
167+
return maybePromiseOrCursor.then((res: unknown) => {
167168
span?.finish();
168169
return res;
169170
});
171+
}
172+
// If the operation returns a cursor (which is an EventEmitter),
173+
// we need to attach a listener to it to finish the span when the cursor is closed.
174+
else if (isInstanceOf(maybePromiseOrCursor, EventEmitter)) {
175+
const cursor = maybePromiseOrCursor as EventEmitter;
176+
177+
try {
178+
cursor.once('close', () => {
179+
span?.finish();
180+
});
181+
} catch (e) {
182+
// If the cursor is already closed, `once` will throw an error. In that case, we can
183+
// finish the span immediately.
184+
span?.finish();
185+
}
186+
187+
return cursor;
170188
} else {
171189
span?.finish();
172-
return maybePromise;
190+
return maybePromiseOrCursor;
173191
}
174192
}
175193

176194
const span = parentSpan?.startChild(getSpanContext(this, operation, args.slice(0, -1)));
195+
177196
return orig.call(this, ...args.slice(0, -1), function (err: Error, result: unknown) {
178197
span?.finish();
179198
lastArg(err, result);

0 commit comments

Comments
 (0)