From f2a1c7fb29c678bcddfb0d6baf8f9e09d8e81bd1 Mon Sep 17 00:00:00 2001 From: lazerg Date: Thu, 29 Oct 2026 12:19:05 +0500 Subject: [PATCH] fix(nuxt): Only create database and storage spans when there is a parent span Co-Authored-By: Claude Opus 5.5 --- .../src/runtime/utils/instrumentDatabase.ts | 1 + .../src/runtime/utils/instrumentStorage.ts | 1 + .../runtime/utils/instrumentDatabase.test.ts | 38 ++++++++++++++++++ .../runtime/utils/instrumentStorage.test.ts | 39 +++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 packages/nuxt/test/runtime/utils/instrumentDatabase.test.ts create mode 100644 packages/nuxt/test/runtime/utils/instrumentStorage.test.ts diff --git a/packages/nuxt/src/runtime/utils/instrumentDatabase.ts b/packages/nuxt/src/runtime/utils/instrumentDatabase.ts index 71149f91ff19..83103f046d13 100644 --- a/packages/nuxt/src/runtime/utils/instrumentDatabase.ts +++ b/packages/nuxt/src/runtime/utils/instrumentDatabase.ts @@ -271,6 +271,7 @@ function createStartSpanOptions( return { name, + onlyIfParent: true, attributes: { [DB_QUERY_TEXT]: queryText, [DB_QUERY_SUMMARY]: querySummary, diff --git a/packages/nuxt/src/runtime/utils/instrumentStorage.ts b/packages/nuxt/src/runtime/utils/instrumentStorage.ts index 1480c1b7b32d..0f8456e04347 100644 --- a/packages/nuxt/src/runtime/utils/instrumentStorage.ts +++ b/packages/nuxt/src/runtime/utils/instrumentStorage.ts @@ -249,6 +249,7 @@ function createSpanStartOptions( return { // With span streaming, span names have to be low cardinality, so we can't fall back to the cache keys. name: client && hasSpanStreamingEnabled(client) ? cacheOperation : keys.join(', '), + onlyIfParent: true, attributes, }; } diff --git a/packages/nuxt/test/runtime/utils/instrumentDatabase.test.ts b/packages/nuxt/test/runtime/utils/instrumentDatabase.test.ts new file mode 100644 index 000000000000..32b9dbffc5d4 --- /dev/null +++ b/packages/nuxt/test/runtime/utils/instrumentDatabase.test.ts @@ -0,0 +1,38 @@ +import { createTransport, getCurrentScope, spanToJSON, startSpan } from '@sentry/core'; +import { ServerRuntimeClient } from '@sentry/core/server'; +import type { Database } from 'db0'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { createDatabasePlugin } from '../../../src/runtime/utils/instrumentDatabase'; + +describe('createDatabasePlugin', () => { + const startedSpans: string[] = []; + + beforeEach(() => { + startedSpans.length = 0; + + const client = new ServerRuntimeClient({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [], + transport: () => createTransport({ recordDroppedEvent: () => undefined }, () => Promise.resolve({})), + stackParser: () => [], + tracesSampleRate: 1, + }); + getCurrentScope().setClient(client); + client.init(); + client.on('spanStart', span => startedSpans.push(spanToJSON(span).name)); + }); + + afterEach(() => { + getCurrentScope().setClient(undefined); + }); + + it('only creates db spans when there is an active parent span', async () => { + const db = { dialect: 'sqlite', exec: vi.fn(), sql: vi.fn(), prepare: vi.fn() } as unknown as Database; + createDatabasePlugin(() => db, { default: {} }); + + await db.exec('SELECT * FROM users'); + await startSpan({ name: 'parent' }, () => db.exec('SELECT * FROM orders')); + + expect(startedSpans).toEqual(['parent', 'SELECT orders']); + }); +}); diff --git a/packages/nuxt/test/runtime/utils/instrumentStorage.test.ts b/packages/nuxt/test/runtime/utils/instrumentStorage.test.ts new file mode 100644 index 000000000000..1340df81bb6c --- /dev/null +++ b/packages/nuxt/test/runtime/utils/instrumentStorage.test.ts @@ -0,0 +1,39 @@ +import { createTransport, getCurrentScope, spanToJSON, startSpan } from '@sentry/core'; +import { ServerRuntimeClient } from '@sentry/core/server'; +import type { Driver, Storage } from 'unstorage'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { createStoragePlugin } from '../../../src/runtime/utils/instrumentStorage'; + +describe('createStoragePlugin', () => { + const startedSpans: string[] = []; + + beforeEach(() => { + startedSpans.length = 0; + + const client = new ServerRuntimeClient({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [], + transport: () => createTransport({ recordDroppedEvent: () => undefined }, () => Promise.resolve({})), + stackParser: () => [], + tracesSampleRate: 1, + }); + getCurrentScope().setClient(client); + client.init(); + client.on('spanStart', span => startedSpans.push(spanToJSON(span).name)); + }); + + afterEach(() => { + getCurrentScope().setClient(undefined); + }); + + it('only creates cache spans when there is an active parent span', async () => { + const driver = { name: 'memory', getItem: vi.fn() } as unknown as Driver; + const storage = { getMounts: () => [{ base: 'cache:', driver }], mount: vi.fn() } as unknown as Storage; + await createStoragePlugin(() => storage, []); + + await driver.getItem('outside', {}); + await startSpan({ name: 'parent' }, () => driver.getItem('inside', {})); + + expect(startedSpans).toEqual(['parent', 'cache.get']); + }); +});