8000 fix(tracing): Instrument cursors returned from MongoDB operations. (#… · jonator/sentry-javascript@329836d · GitHub
[go: up one dir, main page]

Skip to content

Commit 329836d

Browse files
authored
fix(tracing): Instrument cursors returned from MongoDB operations. (getsentry#6437)
1 parent e5c1486 commit 329836d

File tree

3 files changed

+32
-14
lines changed

3 files changed

+32
-14
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: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ interface MongoOptions {
9090
useMongoose?: boolean;
9191
}
9292

93+
interface MongoCursor {
94+
once(event: 'close', listener: () => void): void;
95+
}
96+
97+
function isCursor(maybeCursor: MongoCursor): maybeCursor is MongoCursor {
98+
return maybeCursor && typeof maybeCursor === 'object' && maybeCursor.once && typeof maybeCursor.once === 'function';
99+
}
100+
93101
/** Tracing integration for mongo package */
94102
export class Mongo implements Integration {
95103
/**
@@ -160,20 +168,38 @@ export class Mongo implements Integration {
160168
// its (non-callback) arguments can also be functions.)
161169
if (typeof lastArg !== 'function' || (operation === 'mapReduce' && args.length === 2)) {
162170
const span = parentSpan?.startChild(getSpanContext(this, operation, args));
163-
const maybePromise = orig.call(this, ...args) as Promise<unknown>;
171+
const maybePromiseOrCursor = orig.call(this, ...args);
164172

165-
if (isThenable(maybePromise)) {
166-
return maybePromise.then((res: unknown) => {
173+
if (isThenable(maybePromiseOrCursor)) {
174+
return maybePromiseOrCursor.then((res: unknown) => {
167175
span?.finish();
168176
return res;
169177
});
178+
}
179+
// If the operation returns a Cursor
180+
// we need to attach a listener to it to finish the span when the cursor is closed.
181+
else if (isCursor(maybePromiseOrCursor)) {
182+
const cursor = maybePromiseOrCursor as MongoCursor;
183+
184+
try {
185+
cursor.once('close', () => {
186+
span?.finish();
187+
});
188+
} catch (e) {
189+
// If the cursor is already closed, `once` will throw an error. In that case, we can
190+
// finish the span immediately.
191+
span?.finish();
192+
}
193+
194+
return cursor;
170195
} else {
171196
span?.finish();
172-
return maybePromise;
197+
return maybePromiseOrCursor;
173198
}
174199
}
175200

176201
const span = parentSpan?.startChild(getSpanContext(this, operation, args.slice(0, -1)));
202+
177203
return orig.call(this, ...args.slice(0, -1), function (err: Error, result: unknown) {
178204
span?.finish();
179205
lastArg(err, result);

0 commit comments

Comments
 (0)
0