From 614c69b6e8067e658dd0e3a5bb2ff0ed35a33632 Mon Sep 17 00:00:00 2001 From: Emmanuel T Odeke Date: Tue, 8 Oct 2024 22:50:11 -0700 Subject: [PATCH] feat: (observability) propagate database name for every span generated to aid in quick debugging With this change customers shall always be able to identify which database is being connected to. Updates #2079 --- observability-test/database.ts | 41 +++++++++++-- observability-test/helper.ts | 15 +++++ observability-test/spanner.ts | 51 +++++++++++++++- observability-test/table.ts | 20 ++++++- observability-test/transaction.ts | 8 +++ src/batch-transaction.ts | 8 +++ src/database.ts | 99 +++++++++++++++++-------------- src/session-pool.ts | 7 ++- src/table.ts | 6 ++ src/transaction.ts | 60 +++++++++++++++---- 10 files changed, 253 insertions(+), 62 deletions(-) diff --git a/observability-test/database.ts b/observability-test/database.ts index d4dcea825..fce3ef743 100644 --- a/observability-test/database.ts +++ b/observability-test/database.ts @@ -39,6 +39,7 @@ import {Instance, Spanner} from '../src'; import * as pfy from '@google-cloud/promisify'; import {grpc} from 'google-gax'; import {MockError} from '../test/mockserver/mockspanner'; +const {generateWithAllSpansHaveDBName} = require('./helper'); const fakePfy = extend({}, pfy, { promisifyAll(klass, options) { @@ -235,16 +236,20 @@ describe('Database', () => { DatabaseCached = Object.assign({}, Database); }); + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + INSTANCE.formattedName_ + '/databases/' + NAME + ); + beforeEach(() => { fakeCodec.encode = util.noop; extend(Database, DatabaseCached); - database = new Database(INSTANCE, NAME, POOL_OPTIONS); - database.parent = INSTANCE; - database.databaseRole = 'parent_role'; - database._observabilityOptions = { + INSTANCE._observabilityOptions = { tracerProvider: provider, enableExtendedTracing: false, }; + database = new Database(INSTANCE, NAME, POOL_OPTIONS); + database.parent = INSTANCE; + database.databaseRole = 'parent_role'; const gaxOpts = {}; const options: { a: string; @@ -285,6 +290,8 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); + const actualSpanNames: string[] = []; const actualEventNames: string[] = []; spans.forEach(span => { @@ -333,6 +340,7 @@ describe('Database', () => { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); const actualSpanNames: string[] = []; const actualEventNames: string[] = []; @@ -523,6 +531,7 @@ describe('Database', () => { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); const actualSpanNames: string[] = []; const actualEventNames: string[] = []; @@ -604,6 +613,7 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 2, 'Exactly 2 spans expected'); + withAllSpansHaveDBName(spans); const actualSpanNames: string[] = []; const actualEventNames: string[] = []; @@ -706,6 +716,8 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); + const actualEventNames: string[] = []; const actualSpanNames: string[] = []; spans.forEach(span => { @@ -771,6 +783,7 @@ describe('Database', () => { assert.strictEqual(resp, RESPONSE); const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); const actualEventNames: string[] = []; const actualSpanNames: string[] = []; @@ -836,6 +849,8 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); + const actualSpanNames: string[] = []; const actualEventNames: string[] = []; spans.forEach(span => { @@ -911,6 +926,8 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); + const actualEventNames: string[] = []; const actualSpanNames: string[] = []; spans.forEach(span => { @@ -958,6 +975,8 @@ describe('Database', () => { assert.strictEqual(transaction, fakeTransaction); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); + assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); const actualEventNames: string[] = []; const actualSpanNames: string[] = []; @@ -1037,6 +1056,7 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); const actualEventNames: string[] = []; const actualSpanNames: string[] = []; @@ -1091,6 +1111,7 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); const actualEventNames: string[] = []; const actualSpanNames: string[] = []; @@ -1148,6 +1169,8 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); + const actualSpanNames: string[] = []; const actualEventNames: string[] = []; spans.forEach(span => { @@ -1222,6 +1245,8 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); + const actualSpanNames: string[] = []; const actualEventNames: string[] = []; spans.forEach(span => { @@ -1273,6 +1298,8 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); + const actualSpanNames: string[] = []; const actualEventNames: string[] = []; spans.forEach(span => { @@ -1376,6 +1403,8 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); + const actualEventNames: string[] = []; const actualSpanNames: string[] = []; spans.forEach(span => { @@ -1427,6 +1456,8 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); + const actualEventNames: string[] = []; const actualSpanNames: string[] = []; spans.forEach(span => { @@ -1491,6 +1522,8 @@ describe('Database', () => { const spans = traceExporter.getFinishedSpans(); assert.strictEqual(spans.length, 2, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); + const actualSpanNames: string[] = []; const actualEventNames: string[] = []; spans.forEach(span => { diff --git a/observability-test/helper.ts b/observability-test/helper.ts index 342a413ee..b6d429d32 100644 --- a/observability-test/helper.ts +++ b/observability-test/helper.ts @@ -15,6 +15,9 @@ */ import {ContextManager, context} from '@opentelemetry/api'; +import * as assert from 'assert'; +const {ReadableSpan} = require('@opentelemetry/sdk-trace-base'); +import {SEMATTRS_DB_NAME} from '@opentelemetry/semantic-conventions'; /** * This utility exists as a test helper because mocha has builtin "context" @@ -32,3 +35,15 @@ export function disableContextAndManager(manager: ContextManager) { manager.disable(); context.disable(); } + +export function generateWithAllSpansHaveDBName(dbName: String): Function { + return function (spans: (typeof ReadableSpan)[]) { + spans.forEach(span => { + assert.deepStrictEqual( + span.attributes[SEMATTRS_DB_NAME], + dbName, + `Span ${span.name} has mismatched DB_NAME` + ); + }); + }; +} diff --git a/observability-test/spanner.ts b/observability-test/spanner.ts index 322fcc1b9..93300a1ad 100644 --- a/observability-test/spanner.ts +++ b/observability-test/spanner.ts @@ -30,7 +30,11 @@ const { } = require('@opentelemetry/sdk-trace-node'); // eslint-disable-next-line n/no-extraneous-require const {SimpleSpanProcessor} = require('@opentelemetry/sdk-trace-base'); -const {disableContextAndManager, setGlobalContextManager} = require('./helper'); +const { + disableContextAndManager, + generateWithAllSpansHaveDBName, + setGlobalContextManager, +} = require('./helper'); const { AsyncHooksContextManager, } = require('@opentelemetry/context-async-hooks'); @@ -168,7 +172,13 @@ describe('EndToEnd', () => { it('getSessions', async () => { const [rows] = await database.getSessions(); + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + database.formattedName_ + ); + traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1, 'Exactly 1 span expected'); + withAllSpansHaveDBName(spans); const actualSpanNames: string[] = []; const actualEventNames: string[] = []; @@ -195,6 +205,10 @@ describe('EndToEnd', () => { }); it('getSnapshot', done => { + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + database.formattedName_ + ); + database.getSnapshot((err, transaction) => { assert.ifError(err); @@ -203,6 +217,7 @@ describe('EndToEnd', () => { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); const actualSpanNames: string[] = []; const actualEventNames: string[] = []; @@ -244,12 +259,16 @@ describe('EndToEnd', () => { }); it('getTransaction', done => { + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + database.formattedName_ + ); database.getTransaction((err, transaction) => { assert.ifError(err); assert.ok(transaction); traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); const actualEventNames: string[] = []; const actualSpanNames: string[] = []; @@ -284,6 +303,9 @@ describe('EndToEnd', () => { }); it('runStream', done => { + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + database.formattedName_ + ); database .runStream('SELECT 1') .on('data', row => {}) @@ -291,6 +313,7 @@ describe('EndToEnd', () => { .on('end', () => { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); const actualSpanNames: string[] = []; const actualEventNames: string[] = []; @@ -328,10 +351,14 @@ describe('EndToEnd', () => { }); it('run', async () => { + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + database.formattedName_ + ); const [rows] = await database.run('SELECT 1'); traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); // Sort the spans by duration. spans.sort((spanA, spanB) => { @@ -392,6 +419,9 @@ describe('EndToEnd', () => { }); it('runTransaction', done => { + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + database.formattedName_ + ); database.runTransaction((err, transaction) => { assert.ifError(err); transaction!.run('SELECT 1', (err, rows) => { @@ -399,6 +429,7 @@ describe('EndToEnd', () => { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); const actualEventNames: string[] = []; const actualSpanNames: string[] = []; @@ -438,6 +469,9 @@ describe('EndToEnd', () => { }); it('writeAtLeastOnce', done => { + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + database.formattedName_ + ); const blankMutations = new MutationSet(); database.writeAtLeastOnce(blankMutations, (err, response) => { assert.ifError(err); @@ -445,6 +479,7 @@ describe('EndToEnd', () => { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); const actualEventNames: string[] = []; const actualSpanNames: string[] = []; @@ -700,6 +735,11 @@ describe('ObservabilityOptions injection and propagation', async () => { spannerMock.resetRequests(); }); + const db = spanner.instance('instance').database('database'); + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + db.formattedName_ + ); + it('run', done => { database.getTransaction((err, tx) => { assert.ifError(err); @@ -708,6 +748,7 @@ describe('ObservabilityOptions injection and propagation', async () => { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); const actualSpanNames: string[] = []; const actualEventNames: string[] = []; @@ -761,6 +802,7 @@ describe('ObservabilityOptions injection and propagation', async () => { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); assert.strictEqual(spans.length, 4); const actualSpanNames: string[] = []; @@ -814,6 +856,7 @@ describe('ObservabilityOptions injection and propagation', async () => { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); const actualSpanNames: string[] = []; const actualEventNames: string[] = []; @@ -866,6 +909,7 @@ describe('ObservabilityOptions injection and propagation', async () => { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); + withAllSpansHaveDBName(spans); const actualSpanNames: string[] = []; const actualEventNames: string[] = []; @@ -948,6 +992,10 @@ describe('ObservabilityOptions injection and propagation', async () => { const instance = spanner.instance('instance'); const database = instance.database('database'); + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + database.formattedName_ + ); + database.run('SELECT 1', (err, rows) => { assert.ifError(err); @@ -970,6 +1018,7 @@ describe('ObservabilityOptions injection and propagation', async () => { spansFromInjected.sort((spanA, spanB) => { spanA.startTime < spanB.startTime; }); + withAllSpansHaveDBName(spansFromInjected); const actualSpanNames: string[] = []; const actualEventNames: string[] = []; spansFromInjected.forEach(span => { diff --git a/observability-test/table.ts b/observability-test/table.ts index 86f6145f9..558312c6c 100644 --- a/observability-test/table.ts +++ b/observability-test/table.ts @@ -31,6 +31,7 @@ import {SpanStatusCode} from '@opentelemetry/api'; // eslint-disable-next-line n/no-extraneous-require const {SimpleSpanProcessor} = require('@opentelemetry/sdk-trace-base'); +const {generateWithAllSpansHaveDBName} = require('./helper'); const fakePfy = extend({}, pfy, { promisifyAll(klass, options) { @@ -67,6 +68,7 @@ describe('Table', () => { let transaction: FakeTransaction; const DATABASE = { + formattedName_: 'formatted-db-name', runTransaction: (opts, callback) => callback(null, transaction), getSnapshot: (options, callback) => callback(null, transaction), }; @@ -100,6 +102,10 @@ describe('Table', () => { traceExporter.reset(); }); + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + DATABASE.formattedName_ + ); + function getExportedSpans(minCount: number) { traceExporter.forceFlush(); const spans = traceExporter.getFinishedSpans(); @@ -215,7 +221,10 @@ describe('Table', () => { assert.ifError(err); assert.strictEqual(stub.callCount, 1); - const actualSpanNames = spanNames(getExportedSpans(1)); + const gotSpans = getExportedSpans(1); + withAllSpansHaveDBName(gotSpans); + + const actualSpanNames = spanNames(gotSpans); const expectedSpanNames = ['CloudSpanner.Table.upsert']; assert.deepStrictEqual( @@ -238,6 +247,8 @@ describe('Table', () => { assert.strictEqual(err, fakeError); const gotSpans = getExportedSpans(1); + withAllSpansHaveDBName(gotSpans); + const gotSpanStatus = gotSpans[0].status; const wantSpanStatus = { code: SpanStatusCode.ERROR, @@ -270,7 +281,10 @@ describe('Table', () => { assert.ifError(err); assert.strictEqual(stub.callCount, 1); - const actualSpanNames = spanNames(getExportedSpans(1)); + const gotSpans = getExportedSpans(1); + withAllSpansHaveDBName(gotSpans); + + const actualSpanNames = spanNames(gotSpans); const expectedSpanNames = ['CloudSpanner.Table.replace']; assert.deepStrictEqual( actualSpanNames, @@ -302,6 +316,8 @@ describe('Table', () => { `mismatch in span status:\n\tGot: ${JSON.stringify(gotSpanStatus)}\n\tWant: ${JSON.stringify(wantSpanStatus)}` ); + withAllSpansHaveDBName(gotSpans); + const actualSpanNames = spanNames(gotSpans); const expectedSpanNames = ['CloudSpanner.Table.replace']; assert.deepStrictEqual( diff --git a/observability-test/transaction.ts b/observability-test/transaction.ts index ad1fc0b47..550f85fc3 100644 --- a/observability-test/transaction.ts +++ b/observability-test/transaction.ts @@ -32,6 +32,7 @@ const { ReadableSpan, SimpleSpanProcessor, } = require('@opentelemetry/sdk-trace-base'); +const {generateWithAllSpansHaveDBName} = require('./helper'); describe('Transaction', () => { const sandbox = sinon.createSandbox(); @@ -54,6 +55,10 @@ describe('Transaction', () => { parent: INSTANCE, }; + const withAllSpansHaveDBName = generateWithAllSpansHaveDBName( + DATABASE.formattedName_ + ); + const SESSION = { parent: DATABASE, formattedName_: SESSION_NAME, @@ -723,6 +728,7 @@ describe('Transaction', () => { // Ensure that the final span that got retries did not error. const spans = exportResults.spans; + const firstSpan = spans[0]; assert.strictEqual( SpanStatusCode.ERROR, @@ -734,6 +740,8 @@ describe('Transaction', () => { firstSpan.status.message, 'Unexpected span status message' ); + + withAllSpansHaveDBName(spans); }); }); }); diff --git a/src/batch-transaction.ts b/src/batch-transaction.ts index 403f7dd6e..d182d4429 100644 --- a/src/batch-transaction.ts +++ b/src/batch-transaction.ts @@ -140,6 +140,7 @@ class BatchTransaction extends Snapshot { const traceConfig: traceConfig = { sql: query, opts: this._observabilityOptions, + dbName: this.getDBName(), }; return startTrace( 'BatchTransaction.createQueryPartitions', @@ -170,6 +171,11 @@ class BatchTransaction extends Snapshot { } ); } + + protected getDBName(): string { + return (this.session.parent as Database).formattedName_; + } + /** * Generic create partition method. Handles common parameters used in both * {@link BatchTransaction#createQueryPartitions} and {@link @@ -183,6 +189,7 @@ class BatchTransaction extends Snapshot { createPartitions_(config, callback) { const traceConfig: traceConfig = { opts: this._observabilityOptions, + dbName: this.getDBName(), }; return startTrace( @@ -260,6 +267,7 @@ class BatchTransaction extends Snapshot { createReadPartitions(options, callback) { const traceConfig: traceConfig = { opts: this._observabilityOptions, + dbName: this.getDBName(), }; return startTrace( diff --git a/src/database.ts b/src/database.ts index 9f67bf01b..9a2b703a0 100644 --- a/src/database.ts +++ b/src/database.ts @@ -109,6 +109,7 @@ import { startTrace, setSpanError, setSpanErrorAndException, + traceConfig, } from './instrument'; export type GetDatabaseRolesCallback = RequestCallback< @@ -344,7 +345,8 @@ class Database extends common.GrpcServiceObject { databaseDialect?: EnumKey< typeof databaseAdmin.spanner.admin.database.v1.DatabaseDialect > | null; - _observabilityOptions?: ObservabilityOptions; + _observabilityOptions?: ObservabilityOptions; // TODO: exmaine if we can remove it + private _traceConfig: traceConfig; constructor( instance: Instance, name: string, @@ -460,6 +462,12 @@ class Database extends common.GrpcServiceObject { } this.formattedName_ = formattedName_; this.instance = instance; + this._observabilityOptions = instance._observabilityOptions; + this._traceConfig = { + opts: this._observabilityOptions, + dbName: this.formattedName_, + }; + this.resourceHeader_ = { [CLOUD_RESOURCE_HEADER]: this.formattedName_, }; @@ -682,8 +690,7 @@ class Database extends common.GrpcServiceObject { addLeaderAwareRoutingHeader(headers); } - const traceConfig = {opts: this._observabilityOptions}; - startTrace('Database.batchCreateSessions', traceConfig, span => { + startTrace('Database.batchCreateSessions', this._traceConfig, span => { this.request( { client: 'SpannerClient', @@ -702,7 +709,7 @@ class Database extends common.GrpcServiceObject { const sessions = (resp!.session || []).map(metadata => { const session = this.session(metadata.name!); - session._observabilityOptions = this._observabilityOptions; + session._observabilityOptions = this._traceConfig!.opts; session.metadata = metadata; return session; }); @@ -749,7 +756,7 @@ class Database extends common.GrpcServiceObject { const id = identifier.transaction; const transaction = new BatchTransaction(session, options); transaction.id = id; - transaction._observabilityOptions = this._observabilityOptions; + transaction._observabilityOptions = this._traceConfig!.opts; transaction.readTimestamp = identifier.timestamp as PreciseDate; return transaction; } @@ -838,36 +845,41 @@ class Database extends common.GrpcServiceObject { typeof optionsOrCallback === 'object' ? (optionsOrCallback as TimestampBounds) : {}; - - const traceConfig = {opts: this._observabilityOptions}; - return startTrace('Database.createBatchTransaction', traceConfig, span => { - this.pool_.getSession((err, session) => { - if (err) { - setSpanError(span, err); - span.end(); - callback!(err as ServiceError, null, undefined); - return; - } - const transaction = this.batchTransaction({session: session!}, options); - this._releaseOnEnd(session!, transaction, span); - transaction.begin((err, resp) => { + return startTrace( + 'Database.createBatchTransaction', + this._traceConfig, + span => { + this.pool_.getSession((err, session) => { if (err) { setSpanError(span, err); - if (isSessionNotFoundError(err)) { - span.addEvent('No session available', { - 'session.id': session?.id, - }); - } span.end(); - callback!(err, null, resp!); + callback!(err as ServiceError, null, undefined); return; } - span.addEvent('Using Session', {'session.id': session?.id}); - span.end(); - callback!(null, transaction, resp!); + const transaction = this.batchTransaction( + {session: session!}, + options + ); + this._releaseOnEnd(session!, transaction, span); + transaction.begin((err, resp) => { + if (err) { + setSpanError(span, err); + if (isSessionNotFoundError(err)) { + span.addEvent('No session available', { + 'session.id': session?.id, + }); + } + span.end(); + callback!(err, null, resp!); + return; + } + span.addEvent('Using Session', {'session.id': session?.id}); + span.end(); + callback!(null, transaction, resp!); + }); }); - }); - }); + } + ); } /** * Create a new session. @@ -1097,7 +1109,7 @@ class Database extends common.GrpcServiceObject { /CREATE TABLE `*([^\s`(]+)/ )![1]; const table = this.table(tableName!); - table._observabilityOptions = this._observabilityOptions; + table._observabilityOptions = this._traceConfig!.opts; callback!(null, table, operation!, resp!); }); } @@ -1886,8 +1898,7 @@ class Database extends common.GrpcServiceObject { delete (gaxOpts as GetSessionsOptions).pageToken; } - const traceConfig = {opts: this._observabilityOptions}; - return startTrace('Database.getSessions', traceConfig, span => { + return startTrace('Database.getSessions', this._traceConfig, span => { this.request< google.spanner.v1.ISession, google.spanner.v1.IListSessionsResponse @@ -1908,7 +1919,7 @@ class Database extends common.GrpcServiceObject { sessionInstances = sessions.map(metadata => { const session = self.session(metadata.name!); session.metadata = metadata; - session._observabilityOptions = this._observabilityOptions; + session._observabilityOptions = this._traceConfig!.opts; return session; }); } @@ -2069,8 +2080,7 @@ class Database extends common.GrpcServiceObject { ? (optionsOrCallback as TimestampBounds) : {}; - const traceConfig = {opts: this._observabilityOptions}; - return startTrace('Database.getSnapshot', traceConfig, span => { + return startTrace('Database.getSnapshot', this._traceConfig, span => { this.pool_.getSession((err, session) => { if (err) { setSpanError(span, err); @@ -2170,8 +2180,7 @@ class Database extends common.GrpcServiceObject { ? (optionsOrCallback as GetTransactionOptions) : {}; - const traceConfig = {opts: this._observabilityOptions}; - return startTrace('Database.getTransaction', traceConfig, span => { + return startTrace('Database.getTransaction', this._traceConfig, span => { this.pool_.getSession((err, session, transaction) => { if (options.requestOptions) { transaction!.requestOptions = Object.assign( @@ -2798,7 +2807,10 @@ class Database extends common.GrpcServiceObject { ? (optionsOrCallback as TimestampBounds) : {}; - const traceConfig = {sql: query, opts: this._observabilityOptions}; + const traceConfig = { + sql: query, + ...this._traceConfig, + }; return startTrace('Database.run', traceConfig, span => { this.runStream(query, options) .on('error', err => { @@ -3019,7 +3031,10 @@ class Database extends common.GrpcServiceObject { options?: TimestampBounds ): PartialResultStream { const proxyStream: Transform = through.obj(); - const traceConfig = {sql: query, opts: this._observabilityOptions}; + const traceConfig = { + sql: query, + ...this._traceConfig, + }; return startTrace('Database.runStream', traceConfig, span => { this.pool_.getSession((err, session) => { if (err) { @@ -3197,8 +3212,7 @@ class Database extends common.GrpcServiceObject { ? (optionsOrRunFn as RunTransactionOptions) : {}; - const traceConfig = {opts: this._observabilityOptions}; - startTrace('Database.runTransaction', traceConfig, span => { + startTrace('Database.runTransaction', this._traceConfig, span => { this.pool_.getSession((err, session?, transaction?) => { if (err) { setSpanError(span, err); @@ -3592,8 +3606,7 @@ class Database extends common.GrpcServiceObject { ? (optionsOrCallback as CallOptions) : {}; - const traceConfig = {opts: this._observabilityOptions}; - return startTrace('Database.writeAtLeastOnce', traceConfig, span => { + return startTrace('Database.writeAtLeastOnce', this._traceConfig, span => { this.pool_.getSession((err, session?, transaction?) => { if (err && isSessionNotFoundError(err as grpc.ServiceError)) { span.addEvent('No session available', { diff --git a/src/session-pool.ts b/src/session-pool.ts index 71be508da..b206bcf8e 100644 --- a/src/session-pool.ts +++ b/src/session-pool.ts @@ -756,7 +756,12 @@ export class SessionPool extends EventEmitter implements SessionPoolInterface { let nReturned = 0; const nRequested: number = amount; - const traceConfig = {opts: this._observabilityOptions}; + // TODO: Inlining this code for now and later on shall go + // extract _traceConfig to the constructor when we have plenty of time. + const traceConfig = { + opts: this._observabilityOptions, + dbName: this.database.formattedName_, + }; return startTrace('SessionPool.createSessions', traceConfig, async span => { span.addEvent(`Requesting ${amount} sessions`); diff --git a/src/table.ts b/src/table.ts index 74d0e0375..227f8d107 100644 --- a/src/table.ts +++ b/src/table.ts @@ -191,6 +191,11 @@ class Table { this.database.createTable(schema, gaxOptions, callback!); } + + protected getDBName(): string { + return this.database.formattedName_; + } + /** * Create a readable object stream to receive rows from the database using key * lookups and scans. @@ -1083,6 +1088,7 @@ class Table { const traceConfig: traceConfig = { opts: this._observabilityOptions, tableName: this.name, + dbName: this.getDBName(), }; startTrace('Table.' + method, traceConfig, span => { diff --git a/src/transaction.ts b/src/transaction.ts index 7e0dbb40b..e7993e74c 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -292,6 +292,7 @@ export class Snapshot extends EventEmitter { resourceHeader_: {[k: string]: string}; requestOptions?: Pick; _observabilityOptions?: ObservabilityOptions; + protected _dbName?: string; /** * The transaction ID. @@ -351,8 +352,9 @@ export class Snapshot extends EventEmitter { const readOnly = Snapshot.encodeTimestampBounds(options || {}); this._options = {readOnly}; + this._dbName = (this.session.parent as Database).formattedName_; this.resourceHeader_ = { - [CLOUD_RESOURCE_HEADER]: (this.session.parent as Database).formattedName_, + [CLOUD_RESOURCE_HEADER]: this._dbName, }; this._waitingRequests = []; this._inlineBeginStarted = false; @@ -439,7 +441,10 @@ export class Snapshot extends EventEmitter { addLeaderAwareRoutingHeader(headers); } - const traceConfig = {opts: this._observabilityOptions}; + const traceConfig = { + opts: this._observabilityOptions, + dbName: this._dbName!, + }; return startTrace('Snapshot.begin', traceConfig, span => { span.addEvent('Begin Transaction'); @@ -717,7 +722,11 @@ export class Snapshot extends EventEmitter { }); }; - const traceConfig = {tableName: table, opts: this._observabilityOptions}; + const traceConfig = { + tableName: table, + opts: this._observabilityOptions, + dbName: this._dbName!, + }; return startTrace('Snapshot.createReadStream', traceConfig, span => { const resultStream = partialResultStream( this._wrapWithIdWaiter(makeRequest), @@ -961,7 +970,11 @@ export class Snapshot extends EventEmitter { callback = cb as ReadCallback; } - const traceConfig = {tableName: table, opts: this._observabilityOptions}; + const traceConfig = { + tableName: table, + opts: this._observabilityOptions, + dbName: this._dbName!, + }; return startTrace('Snapshot.read', traceConfig, span => { this.createReadStream(table, request) .on('error', err => { @@ -1065,7 +1078,11 @@ export class Snapshot extends EventEmitter { let stats: google.spanner.v1.ResultSetStats; let metadata: google.spanner.v1.ResultSetMetadata; - const traceConfig = {sql: query, opts: this._observabilityOptions}; + const traceConfig = { + sql: query, + opts: this._observabilityOptions, + dbName: this._dbName!, + }; startTrace('Snapshot.run', traceConfig, span => { return this.runStream(query) .on('error', (err, rows, stats, metadata) => { @@ -1258,7 +1275,11 @@ export class Snapshot extends EventEmitter { addLeaderAwareRoutingHeader(headers); } - const traceConfig = {opts: this._observabilityOptions, ...query}; + const traceConfig = { + opts: this._observabilityOptions, + dbName: this._dbName!, + ...query, + }; return startTrace('Snapshot.runStream', traceConfig, span => { const makeRequest = (resumeToken?: ResumeToken): Readable => { if (!reqOpts || (this.id && !reqOpts.transaction.id)) { @@ -1627,7 +1648,11 @@ export class Dml extends Snapshot { query = {sql: query} as ExecuteSqlRequest; } - const traceConfig = {opts: this._observabilityOptions, ...query}; + const traceConfig = { + opts: this._observabilityOptions, + dbName: this._dbName!, + ...query, + }; return startTrace('Dml.runUpdate', traceConfig, span => { this.run( query, @@ -1904,7 +1929,10 @@ export class Transaction extends Dml { addLeaderAwareRoutingHeader(headers); } - const traceConfig = {opts: this._observabilityOptions}; + const traceConfig = { + opts: this._observabilityOptions, + dbName: this._dbName!, + }; return startTrace('Transaction.batchUpdate', traceConfig, span => { this.request( { @@ -2074,7 +2102,10 @@ export class Transaction extends Dml { const requestOptions = (options as CommitOptions).requestOptions; const reqOpts: CommitRequest = {mutations, session, requestOptions}; - const traceConfig = {opts: this._observabilityOptions}; + const traceConfig = { + opts: this._observabilityOptions, + dbName: this._dbName!, + }; return startTrace('Transaction.commit', traceConfig, span => { if (this.id) { reqOpts.transactionId = this.id as Uint8Array; @@ -2440,7 +2471,10 @@ export class Transaction extends Dml { const callback = typeof gaxOptionsOrCallback === 'function' ? gaxOptionsOrCallback : cb!; - const traceConfig = {opts: this._observabilityOptions}; + const traceConfig = { + opts: this._observabilityOptions, + dbName: this._dbName!, + }; return startTrace('Transaction.rollback', traceConfig, span => { if (!this.id) { const err = new Error( @@ -2933,7 +2967,11 @@ export class PartitionedDml extends Dml { query: string | ExecuteSqlRequest, callback?: RunUpdateCallback ): void | Promise { - const traceConfig = {sql: query, opts: this._observabilityOptions}; + const traceConfig = { + sql: query, + opts: this._observabilityOptions, + dbName: this._dbName!, + }; return startTrace('PartitionedDml.runUpdate', traceConfig, span => { super.runUpdate(query, (err, count) => { if (err) {