Skip to content

Commit 8dc1243

Browse files
committed
feat(observability): trace Transaction
This change adds observability tracing for Transaction along with tests. Updates #2079 Updates #2114 Requires PR #2145.
1 parent 5237e11 commit 8dc1243

File tree

3 files changed

+1367
-271
lines changed

3 files changed

+1367
-271
lines changed

observability-test/spanner.ts

Lines changed: 250 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ const {
3737

3838
const {ObservabilityOptions} = require('../src/instrument');
3939

40+
const selectSql = 'SELECT 1';
41+
const updateSql = 'UPDATE FOO SET BAR=1 WHERE BAZ=2';
42+
4043
/** A simple result set for SELECT 1. */
4144
function createSelect1ResultSet(): protobuf.ResultSet {
4245
const fields = [
@@ -85,8 +88,6 @@ async function setup(
8588
);
8689
});
8790

88-
const selectSql = 'SELECT 1';
89-
const updateSql = 'UPDATE FOO SET BAR=1 WHERE BAZ=2';
9091
spannerMock.putStatementResult(
9192
selectSql,
9293
mock.StatementResult.resultSet(createSelect1ResultSet())
@@ -205,7 +206,6 @@ describe('EndToEnd', () => {
205206

206207
traceExporter.forceFlush();
207208
const spans = traceExporter.getFinishedSpans();
208-
assert.strictEqual(spans.length, 1, 'Exactly 1 span expected');
209209

210210
const actualSpanNames: string[] = [];
211211
const actualEventNames: string[] = [];
@@ -216,14 +216,19 @@ describe('EndToEnd', () => {
216216
});
217217
});
218218

219-
const expectedSpanNames = ['CloudSpanner.Database.getSnapshot'];
219+
const expectedSpanNames = [
220+
'CloudSpanner.Snapshot.begin',
221+
'CloudSpanner.Database.getSnapshot',
222+
'CloudSpanner.Snapshot.runStream',
223+
'CloudSpanner.Snapshot.run',
224+
];
220225
assert.deepStrictEqual(
221226
actualSpanNames,
222227
expectedSpanNames,
223228
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
224229
);
225230

226-
const expectedEventNames = [];
231+
const expectedEventNames = ['Begin Transaction'];
227232
assert.deepStrictEqual(
228233
actualEventNames,
229234
expectedEventNames,
@@ -279,7 +284,6 @@ describe('EndToEnd', () => {
279284
.on('end', () => {
280285
traceExporter.forceFlush();
281286
const spans = traceExporter.getFinishedSpans();
282-
assert.strictEqual(spans.length, 1, 'Exactly 1 span expected');
283287

284288
const actualSpanNames: string[] = [];
285289
const actualEventNames: string[] = [];
@@ -290,7 +294,10 @@ describe('EndToEnd', () => {
290294
});
291295
});
292296

293-
const expectedSpanNames = ['CloudSpanner.Database.runStream'];
297+
const expectedSpanNames = [
298+
'CloudSpanner.Snapshot.runStream',
299+
'CloudSpanner.Database.runStream',
300+
];
294301
assert.deepStrictEqual(
295302
actualSpanNames,
296303
expectedSpanNames,
@@ -313,7 +320,6 @@ describe('EndToEnd', () => {
313320

314321
traceExporter.forceFlush();
315322
const spans = traceExporter.getFinishedSpans();
316-
assert.strictEqual(spans.length, 2, 'Exactly 2 spans expected');
317323

318324
// Sort the spans by duration.
319325
spans.sort((spanA, spanB) => {
@@ -330,6 +336,7 @@ describe('EndToEnd', () => {
330336
});
331337

332338
const expectedSpanNames = [
339+
'CloudSpanner.Snapshot.runStream',
333340
'CloudSpanner.Database.runStream',
334341
'CloudSpanner.Database.run',
335342
];
@@ -375,7 +382,6 @@ describe('EndToEnd', () => {
375382

376383
traceExporter.forceFlush();
377384
const spans = traceExporter.getFinishedSpans();
378-
assert.strictEqual(spans.length, 1, 'Exactly 1 span expected');
379385

380386
const actualEventNames: string[] = [];
381387
const actualSpanNames: string[] = [];
@@ -386,7 +392,11 @@ describe('EndToEnd', () => {
386392
});
387393
});
388394

389-
const expectedSpanNames = ['CloudSpanner.Database.runTransaction'];
395+
const expectedSpanNames = [
396+
'CloudSpanner.Database.runTransaction',
397+
'CloudSpanner.Snapshot.runStream',
398+
'CloudSpanner.Snapshot.run',
399+
];
390400
assert.deepStrictEqual(
391401
actualSpanNames,
392402
expectedSpanNames,
@@ -413,7 +423,6 @@ describe('EndToEnd', () => {
413423

414424
traceExporter.forceFlush();
415425
const spans = traceExporter.getFinishedSpans();
416-
assert.strictEqual(spans.length, 1, 'Exactly 1 span expected');
417426

418427
const actualEventNames: string[] = [];
419428
const actualSpanNames: string[] = [];
@@ -424,14 +433,21 @@ describe('EndToEnd', () => {
424433
});
425434
});
426435

427-
const expectedSpanNames = ['CloudSpanner.Database.writeAtLeastOnce'];
436+
const expectedSpanNames = [
437+
'CloudSpanner.Transaction.commit',
438+
'CloudSpanner.Database.writeAtLeastOnce',
439+
];
428440
assert.deepStrictEqual(
429441
actualSpanNames,
430442
expectedSpanNames,
431443
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
432444
);
433445

434-
const expectedEventNames = ['Using Session'];
446+
const expectedEventNames = [
447+
'Starting Commit',
448+
'Commit Done',
449+
'Using Session',
450+
];
435451
assert.deepStrictEqual(
436452
actualEventNames,
437453
expectedEventNames,
@@ -522,6 +538,226 @@ describe('ObservabilityOptions injection and propagation', async () => {
522538
done();
523539
});
524540

541+
afterEach(async () => {
542+
await injectedTracerProvider.forceFlush();
543+
injectedTraceExporter.reset();
544+
});
545+
546+
let database: Database;
547+
beforeEach(() => {
548+
const instance = spanner.instance('instance');
549+
database = instance.database('db');
550+
});
551+
552+
describe('Transaction', () => {
553+
const traceExporter = injectedTraceExporter;
554+
555+
it('run', done => {
556+
database.getTransaction((err, tx) => {
557+
assert.ifError(err);
558+
559+
tx!.run('SELECT 1', (err, rows) => {
560+
traceExporter.forceFlush();
561+
562+
const spans = traceExporter.getFinishedSpans();
563+
564+
const actualSpanNames: string[] = [];
565+
const actualEventNames: string[] = [];
566+
spans.forEach(span => {
567+
actualSpanNames.push(span.name);
568+
span.events.forEach(event => {
569+
actualEventNames.push(event.name);
570+
});
571+
});
572+
573+
const expectedSpanNames = [
574+
'CloudSpanner.Database.getTransaction',
575+
'CloudSpanner.Snapshot.runStream',
576+
'CloudSpanner.Snapshot.run',
577+
];
578+
assert.deepStrictEqual(
579+
actualSpanNames,
580+
expectedSpanNames,
581+
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
582+
);
583+
584+
const expectedEventNames = [
585+
'Requesting 25 sessions',
586+
'Creating 25 sessions',
587+
'Requested for 25 sessions returned 25',
588+
'Acquiring session',
589+
'Waiting for a session to become available',
590+
'Acquired session',
591+
'Using Session',
592+
'Transaction Creation Done',
593+
];
594+
assert.strictEqual(
595+
actualEventNames.every(value => expectedEventNames.includes(value)),
596+
true,
597+
`Unexpected events:\n\tGot: ${actualEventNames}\n\tWant: ${expectedEventNames}`
598+
);
599+
600+
done();
601+
});
602+
});
603+
});
604+
605+
it('Transaction.begin+Dml.runUpdate', done => {
606+
database.getTransaction((err, tx) => {
607+
assert.ifError(err);
608+
609+
// Firstly erase the prior spans so that we can have only Transaction spans.
610+
traceExporter.reset();
611+
612+
tx!.begin();
613+
tx!.runUpdate(updateSql, (err, rowCount) => {
614+
assert.ifError(err);
615+
616+
traceExporter.forceFlush();
617+
618+
const spans = traceExporter.getFinishedSpans();
619+
assert.strictEqual(spans.length, 4);
620+
621+
const actualSpanNames: string[] = [];
622+
const actualEventNames: string[] = [];
623+
spans.forEach(span => {
624+
actualSpanNames.push(span.name);
625+
span.events.forEach(event => {
626+
actualEventNames.push(event.name);
627+
});
628+
});
629+
630+
const expectedSpanNames = [
631+
'CloudSpanner.Snapshot.begin',
632+
'CloudSpanner.Snapshot.runStream',
633+
'CloudSpanner.Snapshot.run',
634+
'CloudSpanner.Dml.runUpdate',
635+
];
636+
assert.deepStrictEqual(
637+
actualSpanNames,
638+
expectedSpanNames,
639+
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
640+
);
641+
642+
const expectedEventNames = [
643+
'Begin Transaction',
644+
'Transaction Creation Done',
645+
];
646+
assert.strictEqual(
647+
actualEventNames.every(value => expectedEventNames.includes(value)),
648+
true,
649+
`Unexpected events:\n\tGot: ${actualEventNames}\n\tWant: ${expectedEventNames}`
650+
);
651+
652+
done();
653+
});
654+
});
655+
});
656+
657+
it('runStream', done => {
658+
let rowCount = 0;
659+
database.getTransaction((err, tx) => {
660+
assert.ifError(err);
661+
tx!
662+
.runStream(selectSql)
663+
.on('data', () => rowCount++)
664+
.on('error', assert.ifError)
665+
.on('stats', _stats => {})
666+
.on('end', () => {
667+
tx!.end();
668+
669+
traceExporter.forceFlush();
670+
671+
const spans = traceExporter.getFinishedSpans();
672+
673+
const actualSpanNames: string[] = [];
674+
const actualEventNames: string[] = [];
675+
spans.forEach(span => {
676+
actualSpanNames.push(span.name);
677+
span.events.forEach(event => {
678+
actualEventNames.push(event.name);
679+
});
680+
});
681+
682+
const expectedSpanNames = [
683+
'CloudSpanner.Database.getTransaction',
684+
'CloudSpanner.Snapshot.runStream',
685+
];
686+
assert.deepStrictEqual(
687+
actualSpanNames,
688+
expectedSpanNames,
689+
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
690+
);
691+
692+
const expectedEventNames = ['Using Session'];
693+
assert.deepStrictEqual(
694+
actualEventNames,
695+
expectedEventNames,
696+
`Unexpected events:\n\tGot: ${actualEventNames}\n\tWant: ${expectedEventNames}`
697+
);
698+
699+
done();
700+
});
701+
});
702+
});
703+
704+
it('rollback', done => {
705+
database.getTransaction((err, tx) => {
706+
assert.ifError(err);
707+
708+
// Firstly erase the prior spans so that we can have only Transaction spans.
709+
traceExporter.reset();
710+
711+
tx!.begin();
712+
713+
tx!.runUpdate(updateSql, async (err, rowCount) => {
714+
assert.ifError(err);
715+
tx!.rollback(err => {
716+
traceExporter.forceFlush();
717+
718+
const spans = traceExporter.getFinishedSpans();
719+
720+
const actualSpanNames: string[] = [];
721+
const actualEventNames: string[] = [];
722+
spans.forEach(span => {
723+
actualSpanNames.push(span.name);
724+
span.events.forEach(event => {
725+
actualEventNames.push(event.name);
726+
});
727+
});
728+
729+
const expectedSpanNames = [
730+
'CloudSpanner.Snapshot.begin',
731+
'CloudSpanner.Snapshot.runStream',
732+
'CloudSpanner.Snapshot.run',
733+
'CloudSpanner.Dml.runUpdate',
734+
'CloudSpanner.Transaction.rollback',
735+
];
736+
assert.deepStrictEqual(
737+
actualSpanNames,
738+
expectedSpanNames,
739+
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
740+
);
741+
742+
const expectedEventNames = [
743+
'Begin Transaction',
744+
'Transaction Creation Done',
745+
];
746+
assert.strictEqual(
747+
actualEventNames.every(value =>
748+
expectedEventNames.includes(value)
749+
),
750+
true,
751+
`Unexpected events:\n\tGot: ${actualEventNames}\n\tWant: ${expectedEventNames}`
752+
);
753+
754+
done();
755+
});
756+
});
757+
});
758+
});
759+
});
760+
525761
it('Propagates spans to the injected not global TracerProvider', done => {
526762
const instance = spanner.instance('instance');
527763
const database = instance.database('database');
@@ -558,6 +794,7 @@ describe('ObservabilityOptions injection and propagation', async () => {
558794
});
559795

560796
const expectedSpanNames = [
797+
'CloudSpanner.Snapshot.runStream',
561798
'CloudSpanner.Database.runStream',
562799
'CloudSpanner.Database.run',
563800
];

0 commit comments

Comments
 (0)