Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as Sentry from '@sentry/node';
import { waitForConnection } from '@sentry-internal/node-integration-tests';
import { Connection, Request } from 'tedious';

function connect() {
return new Promise((resolve, reject) => {
const connection = new Connection({
server: '127.0.0.1',
authentication: {
type: 'default',
options: { userName: 'sa', password: 'TESTing123' },
},
options: { port: 1433, encrypt: false },
});

connection.on('connect', err => {
if (err) {
connection.close();
reject(err);
} else {
resolve(connection);
}
});
connection.connect();
});
}

function query(connection, sql) {
return new Promise((resolve, reject) => {
connection.execSql(new Request(sql, err => (err ? reject(err) : resolve())));
});
}

async function run() {
const connection = await waitForConnection(connect);

await query(connection, 'SELECT 1 AS before_parent');

const parent = Sentry.startInactiveSpan({ name: 'parent operation' });
await Sentry.withActiveSpan(parent, () => query(connection, 'SELECT 2 AS inside_parent'));

await query(connection, 'SELECT 3 AS after_parent');
connection.close();

// Flush every query before the parent signals that span collection is complete.
await Sentry.flush();
parent.end();
await Sentry.flush();
}

run();
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { SerializedStreamedSpan } from '@sentry/core';
import {
DB_NAMESPACE,
DB_QUERY_SUMMARY,
Expand Down Expand Up @@ -122,6 +123,44 @@ describeWithDockerCompose('tedious auto instrumentation', { workingDirectory: [_
});
});

createEsmAndCjsTests(__dirname, 'scenario-no-parent.mjs', 'instrument.mjs', (createTestRunner, test) => {
test('only instruments queries with an active parent span', async () => {
const runner = createTestRunner();
const dbSpans = new Map<string, SerializedStreamedSpan>();
const spansPromise = runner.collectStreamedSpans(spans => {
for (const span of spans) {
if (span.attributes[SENTRY_ORIGIN]?.value === ORIGIN) {
dbSpans.set(span.span_id, span);
}
}
return spans.some(span => span.is_segment && span.name === 'parent operation');
});

await runner.unordered().start().completed();

const spans = await spansPromise;
const parent = spans.find(span => span.is_segment && span.name === 'parent operation');
expect(parent).toBeDefined();
expect(
Array.from(dbSpans.values(), span => ({
query: span.attributes[DB_QUERY_TEXT]?.value,
database: span.attributes[DB_NAMESPACE]?.value,
parentSpanId: span.parent_span_id,
traceId: span.trace_id,
isSegment: span.is_segment,
})),
).toEqual([
{
query: 'SELECT ? AS inside_parent',
database: 'master',
parentSpanId: parent?.span_id,
traceId: parent?.trace_id,
isSegment: false,
},
]);
});
});

// tedious 20 requires Node >= 22.
describe.runIf(supports({ min: 22 }))('tedious v20', () => {
createEsmAndCjsTests(
Expand Down
5 changes: 5 additions & 0 deletions packages/server-utils/src/integrations/tedious.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as diagnosticsChannel from '../utils/diagnosticsChannel';
import type { IntegrationFn, SpanAttributes } from '@sentry/core';
import {
defineIntegration,
getActiveSpan,
getClient,
hasSpanStreamingEnabled,
SPAN_STATUS_ERROR,
Expand Down Expand Up @@ -110,6 +111,10 @@ function subscribeConnect(): void {

function subscribeQuery(channelName: string, operation: string): void {
diagnosticsChannel.tracingChannel(channelName).start.subscribe(message => {
if (!getActiveSpan()) {
return;
}

const data = message as TediousQueryChannelContext;
const connection = data.self;
const request = data.arguments[0];
Expand Down
Loading