Skip to content

Commit 7b172a8

Browse files
committed
Test propagation of spans to injected not global TracerProvider
1 parent 0240b49 commit 7b172a8

File tree

6 files changed

+164
-81
lines changed

6 files changed

+164
-81
lines changed

observability-test/database.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ describe('Database', () => {
241241
database = new Database(INSTANCE, NAME, POOL_OPTIONS);
242242
database.parent = INSTANCE;
243243
database.databaseRole = 'parent_role';
244-
database.observabilityConfig = {
244+
database.observabilityOptions_ = {
245245
tracerProvider: provider,
246246
enableExtendedTracing: false,
247247
};

observability-test/spanner.ts

Lines changed: 148 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import * as assert from 'assert';
1818
import {grpc} from 'google-gax';
1919
import {google} from '../protos/protos';
20-
import {Database, Spanner} from '../src';
20+
import {Database, Instance, Spanner} from '../src';
2121
import {MutationSet} from '../src/transaction';
2222
import protobuf = google.spanner.v1;
2323
import * as mock from '../test/mockserver/mockspanner';
@@ -35,6 +35,8 @@ const {
3535
AsyncHooksContextManager,
3636
} = require('@opentelemetry/context-async-hooks');
3737

38+
const {ObservabilityOptions} = require('../src/instrument');
39+
3840
/** A simple result set for SELECT 1. */
3941
function createSelect1ResultSet(): protobuf.ResultSet {
4042
const fields = [
@@ -60,7 +62,9 @@ interface setupResults {
6062
spannerMock: mock.MockSpanner;
6163
}
6264

63-
async function setup(): Promise<setupResults> {
65+
async function setup(
66+
observabilityOptions?: typeof ObservabilityOptions
67+
): Promise<setupResults> {
6468
const server = new grpc.Server();
6569

6670
const spannerMock = mock.createMockSpanner(server);
@@ -97,6 +101,7 @@ async function setup(): Promise<setupResults> {
97101
servicePath: 'localhost',
98102
port,
99103
sslCreds: grpc.credentials.createInsecure(),
104+
observabilityOptions: observabilityOptions,
100105
});
101106

102107
return Promise.resolve({
@@ -149,7 +154,7 @@ describe('EndToEnd', () => {
149154

150155
const instance = spanner.instance('instance');
151156
database = instance.database('database');
152-
database.observabilityConfig = {
157+
database.observabilityOptions_ = {
153158
tracerProvider: provider,
154159
enableExtendedTracing: false,
155160
};
@@ -440,3 +445,143 @@ describe('EndToEnd', () => {
440445
});
441446
});
442447
});
448+
449+
describe('ObservabilityOptions injection and propagation', async () => {
450+
const globalTraceExporter = new InMemorySpanExporter();
451+
const globalTracerProvider = new NodeTracerProvider({
452+
sampler: new AlwaysOnSampler(),
453+
exporter: globalTraceExporter,
454+
});
455+
globalTracerProvider.addSpanProcessor(
456+
new SimpleSpanProcessor(globalTraceExporter)
457+
);
458+
globalTracerProvider.register();
459+
460+
const injectedTraceExporter = new InMemorySpanExporter();
461+
const injectedTracerProvider = new NodeTracerProvider({
462+
sampler: new AlwaysOnSampler(),
463+
exporter: injectedTraceExporter,
464+
});
465+
injectedTracerProvider.addSpanProcessor(
466+
new SimpleSpanProcessor(injectedTraceExporter)
467+
);
468+
469+
const observabilityOptions: typeof ObservabilityOptions = {
470+
tracerProvider: injectedTracerProvider,
471+
enableExtendedTracing: true,
472+
};
473+
474+
const setupResult = await setup(observabilityOptions);
475+
const spanner = setupResult.spanner;
476+
const server = setupResult.server;
477+
const spannerMock = setupResult.spannerMock;
478+
479+
after(async () => {
480+
globalTraceExporter.reset();
481+
injectedTraceExporter.reset();
482+
await globalTracerProvider.shutdown();
483+
await injectedTracerProvider.shutdown();
484+
spannerMock.resetRequests();
485+
spanner.close();
486+
server.tryShutdown(() => {});
487+
});
488+
489+
it('Passed into Spanner, Instance and Database', done => {
490+
// Ensure that the same observability configuration is set on the Spanner client.
491+
assert.deepStrictEqual(spanner.observabilityOptions_, observabilityOptions);
492+
493+
// Acquire a handle to the Instance through spanner.instance.
494+
const instanceByHandle = spanner.instance('instance');
495+
assert.deepStrictEqual(
496+
instanceByHandle.observabilityOptions_,
497+
observabilityOptions
498+
);
499+
500+
// Create the Instance by means of a constructor directly.
501+
const instanceByConstructor = new Instance(spanner, 'myInstance');
502+
assert.deepStrictEqual(
503+
instanceByConstructor.observabilityOptions_,
504+
observabilityOptions
505+
);
506+
507+
// Acquire a handle to the Database through instance.database.
508+
const databaseByHandle = instanceByHandle.database('database');
509+
assert.deepStrictEqual(
510+
databaseByHandle.observabilityOptions_,
511+
observabilityOptions
512+
);
513+
514+
// Create the Database by means of a constructor directly.
515+
const databaseByConstructor = new Database(
516+
instanceByConstructor,
517+
'myDatabase'
518+
);
519+
assert.deepStrictEqual(
520+
databaseByConstructor.observabilityOptions_,
521+
observabilityOptions
522+
);
523+
524+
done();
525+
});
526+
527+
it('Propagates spans to the injected not global TracerProvider', done => {
528+
const instance = spanner.instance('instance');
529+
const database = instance.database('database');
530+
531+
database.run('SELECT 1', (err, rows) => {
532+
assert.ifError(err);
533+
534+
injectedTraceExporter.forceFlush();
535+
globalTraceExporter.forceFlush();
536+
const spansFromInjected = injectedTraceExporter.getFinishedSpans();
537+
const spansFromGlobal = globalTraceExporter.getFinishedSpans();
538+
539+
assert.strictEqual(
540+
spansFromGlobal.length,
541+
0,
542+
'Expecting no spans from the global exporter'
543+
);
544+
assert.strictEqual(
545+
spansFromInjected.length > 0,
546+
true,
547+
'Expecting spans from the injected exporter'
548+
);
549+
550+
spansFromInjected.sort((spanA, spanB) => {
551+
spanA.startTime < spanB.startTime;
552+
});
553+
const actualSpanNames: string[] = [];
554+
const actualEventNames: string[] = [];
555+
spansFromInjected.forEach(span => {
556+
actualSpanNames.push(span.name);
557+
span.events.forEach(event => {
558+
actualEventNames.push(event.name);
559+
});
560+
});
561+
562+
const expectedSpanNames = [
563+
'CloudSpanner.Database.runStream',
564+
'CloudSpanner.Database.run',
565+
];
566+
assert.deepStrictEqual(
567+
actualSpanNames,
568+
expectedSpanNames,
569+
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
570+
);
571+
572+
const expectedEventNames = [
573+
'Acquiring session',
574+
'Waiting for a session to become available',
575+
'Acquired session',
576+
'Using Session',
577+
];
578+
assert.deepStrictEqual(
579+
actualEventNames,
580+
expectedEventNames,
581+
`Unexpected events:\n\tGot: ${actualEventNames}\n\tWant: ${expectedEventNames}`
582+
);
583+
584+
done();
585+
});
586+
});
587+
});

samples/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
"@google-cloud/kms": "^4.0.0",
1919
"@google-cloud/precise-date": "^4.0.0",
2020
"@google-cloud/spanner": "^7.14.0",
21-
"yargs": "^17.0.0",
22-
"protobufjs": "^7.0.0"
21+
"protobufjs": "^7.0.0",
22+
"yargs": "^17.0.0"
2323
},
2424
"devDependencies": {
2525
"chai": "^4.2.0",

src/database.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ class Database extends common.GrpcServiceObject {
693693

694694
const sessions = (resp!.session || []).map(metadata => {
695695
const session = this.session(metadata.name!);
696-
session.observabilityConfig = this.observabilityConfig;
696+
session.observabilityOptions_ = this.observabilityOptions_;
697697
session.metadata = metadata;
698698
return session;
699699
});
@@ -738,6 +738,7 @@ class Database extends common.GrpcServiceObject {
738738
const id = identifier.transaction;
739739
const transaction = new BatchTransaction(session, options);
740740
transaction.id = id;
741+
transaction.observabilityOptions_ = this.observabilityOptions_;
741742
transaction.readTimestamp = identifier.timestamp as PreciseDate;
742743
return transaction;
743744
}
@@ -827,7 +828,7 @@ class Database extends common.GrpcServiceObject {
827828
? (optionsOrCallback as TimestampBounds)
828829
: {};
829830

830-
const q = {opts: this.observabilityConfig};
831+
const q = {opts: this.observabilityOptions_};
831832
return startTrace('Database.createBatchTransaction', q, span => {
832833
this.pool_.getSession((err, session) => {
833834
if (err) {
@@ -1874,7 +1875,7 @@ class Database extends common.GrpcServiceObject {
18741875
delete (gaxOpts as GetSessionsOptions).pageToken;
18751876
}
18761877

1877-
const q = {opts: this.observabilityConfig};
1878+
const q = {opts: this.observabilityOptions_};
18781879
return startTrace('Database.getSessions', q, span => {
18791880
this.request<
18801881
google.spanner.v1.ISession,
@@ -1896,7 +1897,7 @@ class Database extends common.GrpcServiceObject {
18961897
sessionInstances = sessions.map(metadata => {
18971898
const session = self.session(metadata.name!);
18981899
session.metadata = metadata;
1899-
session.observabilityConfig = this.observabilityConfig;
1900+
session.observabilityOptions_ = this.observabilityOptions_;
19001901
return session;
19011902
});
19021903
}
@@ -2057,7 +2058,7 @@ class Database extends common.GrpcServiceObject {
20572058
? (optionsOrCallback as TimestampBounds)
20582059
: {};
20592060

2060-
const q = {opts: this.observabilityConfig};
2061+
const q = {opts: this.observabilityOptions_};
20612062
return startTrace('Database.getSnapshot', q, span => {
20622063
this.pool_.getSession((err, session) => {
20632064
if (err) {
@@ -2158,7 +2159,7 @@ class Database extends common.GrpcServiceObject {
21582159
? (optionsOrCallback as GetTransactionOptions)
21592160
: {};
21602161

2161-
const q = {opts: this.observabilityConfig};
2162+
const q = {opts: this.observabilityOptions_};
21622163
return startTrace('Database.getTransaction', q, span => {
21632164
this.pool_.getSession((err, session, transaction) => {
21642165
if (options.requestOptions) {
@@ -2785,7 +2786,7 @@ class Database extends common.GrpcServiceObject {
27852786
? (optionsOrCallback as TimestampBounds)
27862787
: {};
27872788

2788-
const q = {sql: query, opts: this.observabilityConfig};
2789+
const q = {sql: query, opts: this.observabilityOptions_};
27892790
return startTrace('Database.run', q, span => {
27902791
this.runStream(query, options)
27912792
.on('error', err => {
@@ -3006,7 +3007,7 @@ class Database extends common.GrpcServiceObject {
30063007
options?: TimestampBounds
30073008
): PartialResultStream {
30083009
const proxyStream: Transform = through.obj();
3009-
const q = {sql: query, opts: this.observabilityConfig};
3010+
const q = {sql: query, opts: this.observabilityOptions_};
30103011
return startTrace('Database.runStream', q, span => {
30113012
this.pool_.getSession((err, session) => {
30123013
if (err) {
@@ -3184,7 +3185,7 @@ class Database extends common.GrpcServiceObject {
31843185
? (optionsOrRunFn as RunTransactionOptions)
31853186
: {};
31863187

3187-
const q = {opts: this.observabilityConfig};
3188+
const q = {opts: this.observabilityOptions_};
31883189
startTrace('Database.runTransaction', q, span => {
31893190
this.pool_.getSession((err, session?, transaction?) => {
31903191
if (err) {
@@ -3577,7 +3578,7 @@ class Database extends common.GrpcServiceObject {
35773578
? (optionsOrCallback as CallOptions)
35783579
: {};
35793580

3580-
const q = {opts: this.observabilityConfig};
3581+
const q = {opts: this.observabilityOptions_};
35813582
return startTrace('Database.writeAtLeastOnce', q, span => {
35823583
this.pool_.getSession((err, session?, transaction?) => {
35833584
if (err && isSessionNotFoundError(err as grpc.ServiceError)) {

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,3 +2057,4 @@ import IInstanceConfig = instanceAdmin.spanner.admin.instance.v1.IInstanceConfig
20572057
export {v1, protos};
20582058
export default {Spanner};
20592059
export {Float32, Float, Int, Struct, Numeric, PGNumeric, SpannerDate};
2060+
export {ObservabilityOptions};

test/spanner.ts

Lines changed: 1 addition & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5015,7 +5015,7 @@ describe('Spanner with mock server', () => {
50155015
const opts: typeof ObservabilityOptions = {tracerProvider: provider};
50165016
startTrace('aSpan', {opts: opts}, span => {
50175017
const database = newTestDatabase();
5018-
database.observabilityConfig = opts;
5018+
database.observabilityOptions_ = opts;
50195019

50205020
async function runIt() {
50215021
const query = {
@@ -5054,70 +5054,6 @@ describe('Spanner with mock server', () => {
50545054
);
50555055
});
50565056
});
5057-
5058-
it('Should use passed ObservabilityOptions in Spanner, Instance and Database', () => {
5059-
const exporter = new InMemorySpanExporter();
5060-
const provider = new NodeTracerProvider({
5061-
sampler: new AlwaysOnSampler(),
5062-
exporter: exporter,
5063-
});
5064-
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
5065-
5066-
const observabilityOptions: typeof ObservabilityOptions = {
5067-
tracerProvider: provider,
5068-
enableExtendedTracing: true,
5069-
};
5070-
const spanner = new Spanner({
5071-
servicePath: 'localhost',
5072-
port,
5073-
sslCreds: grpc.credentials.createInsecure(),
5074-
observabilityOptions: observabilityOptions,
5075-
});
5076-
5077-
// Ensure that the same observability configuration is set on the Spanner client.
5078-
assert.deepStrictEqual(spanner.observabilityOptions_, observabilityOptions);
5079-
5080-
// Acquire a handle to the Instance through spanner.instance.
5081-
const instanceByHandle = spanner.instance('instance');
5082-
assert.deepStrictEqual(
5083-
instanceByHandle.observabilityOptions_,
5084-
observabilityOptions
5085-
);
5086-
5087-
// Create the Instance by means of a constructor directly.
5088-
const instanceByConstructor = new Instance(spanner, 'myInstance');
5089-
assert.deepStrictEqual(
5090-
instanceByConstructor.observabilityOptions_,
5091-
observabilityOptions
5092-
);
5093-
5094-
// Acquire a handle to the Database through instance.database.
5095-
const databaseByHandle = instanceByHandle.database('database');
5096-
assert.deepStrictEqual(
5097-
databaseByHandle.observabilityOptions_,
5098-
observabilityOptions
5099-
);
5100-
5101-
// Create the Database by means of a constructor directly.
5102-
const databaseByConstructor = new Database(
5103-
instanceByConstructor,
5104-
'myDatabase'
5105-
);
5106-
assert.deepStrictEqual(
5107-
databaseByConstructor.observabilityOptions_,
5108-
observabilityOptions
5109-
);
5110-
5111-
spanner.close();
5112-
5113-
/*
5114-
* TODO: Once we've merged in end-to-end Database tests,
5115-
* we shall add another test in which we:
5116-
* Register a global exporter
5117-
* Inject a different exporter via Spanner
5118-
* Verify that injected exporter is used and not global.
5119-
*/
5120-
});
51215057
});
51225058

51235059
function executeSimpleUpdate(

0 commit comments

Comments
 (0)