diff --git a/observability-test/helper.ts b/observability-test/helper.ts new file mode 100644 index 000000000..342a413ee --- /dev/null +++ b/observability-test/helper.ts @@ -0,0 +1,34 @@ +/*! + * Copyright 2024 Google LLC. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {ContextManager, context} from '@opentelemetry/api'; + +/** + * This utility exists as a test helper because mocha has builtin "context" + * and referring to context causes type/value collision errors. + */ +export function setGlobalContextManager(manager: ContextManager) { + context.setGlobalContextManager(manager); +} + +/** + * This utility exists as a test helper because mocha has builtin "context" + * and referring to context causes type/value collision errors. + */ +export function disableContextAndManager(manager: ContextManager) { + manager.disable(); + context.disable(); +} diff --git a/observability-test/observability.ts b/observability-test/observability.ts new file mode 100644 index 000000000..94c323ed7 --- /dev/null +++ b/observability-test/observability.ts @@ -0,0 +1,448 @@ +/*! + * Copyright 2024 Google LLC. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); +const { + AlwaysOffSampler, + AlwaysOnSampler, + NodeTracerProvider, + InMemorySpanExporter, +} = require('@opentelemetry/sdk-trace-node'); +const {SpanStatusCode, TracerProvider} = require('@opentelemetry/api'); +// eslint-disable-next-line n/no-extraneous-require +const {SimpleSpanProcessor} = require('@opentelemetry/sdk-trace-base'); +const { + SPAN_NAMESPACE_PREFIX, + getActiveOrNoopSpan, + setSpanError, + setSpanErrorAndException, + startTrace, +} = require('../src/instrument'); +const { + SEMATTRS_DB_NAME, + SEMATTRS_DB_SQL_TABLE, + SEMATTRS_DB_STATEMENT, + SEMATTRS_DB_SYSTEM, + SEMATTRS_EXCEPTION_MESSAGE, +} = require('@opentelemetry/semantic-conventions'); + +const {disableContextAndManager, setGlobalContextManager} = require('./helper'); + +const { + AsyncHooksContextManager, +} = require('@opentelemetry/context-async-hooks'); + +describe('startTrace', () => { + const globalExporter = new InMemorySpanExporter(); + const sampler = new AlwaysOnSampler(); + + const globalProvider = new NodeTracerProvider({ + sampler: sampler, + exporter: globalExporter, + }); + globalProvider.addSpanProcessor(new SimpleSpanProcessor(globalExporter)); + globalProvider.register(); + + const contextManager = new AsyncHooksContextManager(); + setGlobalContextManager(contextManager); + + afterEach(() => { + globalExporter.forceFlush(); + }); + + after(async () => { + globalExporter.forceFlush(); + await globalProvider.shutdown(); + disableContextAndManager(contextManager); + }); + + it('with TracerProvider in global configuration', () => { + startTrace('mySpan', {}, span => { + span.end(); + + const spans = globalExporter.getFinishedSpans(); + assert.strictEqual( + spans.length, + 1, + 'Exactly 1 span must have been exported' + ); + const span0 = spans[0]; + + assert.equal( + span0.name, + SPAN_NAMESPACE_PREFIX + '.mySpan', + 'name mismatch' + ); + }); + }); + + it('with TracerProvider in options, skips using global TracerProvider', () => { + const overridingExporter = new InMemorySpanExporter(); + const overridingProvider = new NodeTracerProvider({ + sampler: sampler, + exporter: overridingExporter, + }); + overridingProvider.addSpanProcessor( + new SimpleSpanProcessor(overridingExporter) + ); + + startTrace( + 'aSpan', + {opts: {tracerProvider: overridingProvider}}, + async span => { + await new Promise((resolve, reject) => setTimeout(resolve, 400)); + span.end(); + + const gotSpansFromGlobal = globalExporter.getFinishedSpans(); + assert.strictEqual( + gotSpansFromGlobal.length, + 0, + 'Expected no spans from the global tracerProvider and exporter but got ${gotSpansFromGlobal.length}' + ); + + const gotSpansFromCurrent = overridingExporter.getFinishedSpans(); + assert.strictEqual( + gotSpansFromCurrent.length, + 1, + 'Expected exactly 1 span but got ${gotSpansFromCurrent.length}' + ); + + overridingExporter.forceFlush(); + await overridingProvider.shutdown(); + } + ); + }); + + it('with semantic attributes', () => { + const opts = {tableName: 'table', dbName: 'db'}; + startTrace('aSpan', opts, span => { + assert.equal( + span.attributes[SEMATTRS_DB_SYSTEM], + 'spanner', + 'Missing DB_SYSTEM attribute' + ); + + assert.equal( + span.attributes[SEMATTRS_DB_SQL_TABLE], + 'table', + 'Missing DB_SQL_TABLE attribute' + ); + + assert.equal( + span.attributes[SEMATTRS_DB_NAME], + 'db', + 'Missing DB_NAME attribute' + ); + }); + }); + + it('with enableExtendedTracing=true, no sql value set', () => { + const opts = {opts: {enableExtendedTracing: true}}; + startTrace('aSpan', opts, span => { + assert.equal( + span.attributes[SEMATTRS_DB_STATEMENT], + undefined, + 'Unexpected DB_STATEMENT attribute' + ); + }); + }); + + it('with enableExtendedTracing=true, sql string value set', () => { + const opts = { + opts: {enableExtendedTracing: true}, + sql: 'SELECT CURRENT_TIMESTAMP()', + }; + + startTrace('aSpan', opts, span => { + assert.equal( + span.attributes[SEMATTRS_DB_STATEMENT], + 'SELECT CURRENT_TIMESTAMP()', + 'Mismatched DB_STATEMENT attribute' + ); + }); + }); + + it('with enableExtendedTracing=false, sql string value set', () => { + const opts = { + opts: {enableExtendedTracing: false}, + sql: 'SELECt CURRENT_TIMESTAMP()', + }; + + startTrace('aSpan', opts, span => { + assert.equal( + span.attributes[SEMATTRS_DB_STATEMENT], + undefined, + 'Mismatched DB_STATEMENT attribute' + ); + }); + }); + + it('with enableExtendedTracing=true, sql ExecuteSqlRequest value set', () => { + const req = {sql: 'SELECT 1=1'}; + const opts = { + opts: {enableExtendedTracing: true}, + sql: req, + }; + + startTrace('aSpan', opts, span => { + assert.equal( + span.attributes[SEMATTRS_DB_STATEMENT], + 'SELECT 1=1', + 'Mismatched DB_STATEMENT attribute' + ); + }); + }); + + it('with enableExtendedTracing=false, sql ExecuteSqlRequest value set', () => { + const req = {sql: 'SELECT 1=1'}; + const opts = { + opts: {enableExtendedTracing: true}, + sql: req, + }; + + startTrace('aSpan', opts, span => { + assert.equal( + span.attributes[SEMATTRS_DB_STATEMENT], + req.sql, + 'Mismatched DB_STATEMENT attribute' + ); + }); + }); + + it('alwaysOffSampler used, no spans exported', () => { + const overridingExporter = new InMemorySpanExporter(); + const overridingProvider = new NodeTracerProvider({ + sampler: new AlwaysOffSampler(), + exporter: overridingExporter, + }); + overridingProvider.addSpanProcessor( + new SimpleSpanProcessor(overridingExporter) + ); + overridingProvider.register(); + + startTrace( + 'aSpan', + {opts: {tracerProvider: overridingProvider}}, + async span => { + await new Promise((resolve, reject) => setTimeout(resolve, 400)); + span.end(); + + const gotSpansFromGlobal = globalExporter.getFinishedSpans(); + assert.strictEqual( + gotSpansFromGlobal.length, + 0, + 'Expected no spans but got ${gotSpansFromGlobal.length}' + ); + + const gotSpansFromCurrent = overridingExporter.getFinishedSpans(); + assert.strictEqual( + gotSpansFromCurrent.length, + 0, + 'Expected no spans but got ${gotSpansFromCurrent.length}' + ); + + overridingExporter.forceFlush(); + await overridingProvider.shutdown(); + } + ); + }); +}); + +describe('getActiveOrNoopSpan', () => { + let globalProvider: typeof TracerProvider; + let exporter: typeof InMemorySpanExporter; + + before(() => { + exporter = new InMemorySpanExporter(); + globalProvider = new NodeTracerProvider({ + sampler: new AlwaysOffSampler(), + exporter: exporter, + }); + globalProvider.addSpanProcessor(new SimpleSpanProcessor(exporter)); + globalProvider.register(); + }); + + beforeEach(() => { + exporter.forceFlush(); + }); + + after(async () => { + await globalProvider.shutdown(); + }); + + it('with no value should return a noopSpan and nothing exported', () => { + const span = getActiveOrNoopSpan(); + assert.strictEqual(!span, false, 'the span MUST not be null regardless'); + span.updateName('aSpan should not crash'); + span.setStatus({message: 'done here'}); + }); + + it('with a started span should return the currently active one', () => { + startTrace('aSpan', {}, span => { + const activeSpan = getActiveOrNoopSpan(); + assert.strictEqual( + span.name, + SPAN_NAMESPACE_PREFIX + '.aSpan', + 'names must match' + ); + assert.strictEqual( + span.name, + activeSpan.name, + 'names must match between activeSpan or current one' + ); + assert.strictEqual( + span.startTime, + activeSpan.startTime, + 'startTimes must match' + ); + assert.ok( + span.duration, + undefined, + 'the unended span must have an undefined duration' + ); + assert.ok( + activeSpan.duration, + undefined, + 'the unended span must have an undefined duration, got ${activeSpan.duration}' + ); + assert.strictEqual( + span.duration, + activeSpan.duration, + 'durations must match' + ); + span.end(); + }); + }); +}); + +describe('setError', () => { + const exporter = new InMemorySpanExporter(); + const provider = new NodeTracerProvider({ + sampler: new AlwaysOnSampler(), + exporter: exporter, + }); + provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); + provider.register(); + + const contextManager = new AsyncHooksContextManager(); + setGlobalContextManager(contextManager); + + afterEach(() => { + exporter.forceFlush(); + }); + + after(async () => { + exporter.forceFlush(); + await provider.shutdown(); + disableContextAndManager(contextManager); + }); + + it('passing in null error or null span should have no effect', () => { + startTrace('aSpan', {opts: {tracerProvider: provider}}, span => { + const status1 = span.status; + let res = setSpanError(span, null); + assert.strictEqual(res, false, 'nothing was set'); + const status2 = span.status; + assert.strictEqual( + status1, + status2, + 'setting null error should have no effect' + ); + + res = setSpanError(null, null); + assert.strictEqual(res, false, 'nothing was set'); + }); + }); + + it('a non-empty string should set the message', () => { + startTrace('aSpan', {opts: {tracerProvider: provider}}, span => { + const status1 = span.status; + const res = setSpanError(span, 'this one'); + assert.strictEqual(res, true, 'value was set'); + span.end(); + + const spans = exporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1, 'exactly 1 span must be exported'); + + const expSpan = spans[0]; + const status2 = expSpan.status; + assert.strictEqual(status2.message, 'this one'); + assert.strictEqual(status2.code, SpanStatusCode.ERROR); + }); + }); +}); + +describe('setErrorAndException', () => { + const exporter = new InMemorySpanExporter(); + const provider = new NodeTracerProvider({ + sampler: new AlwaysOnSampler(), + exporter: exporter, + }); + provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); + provider.register(); + + const contextManager = new AsyncHooksContextManager(); + setGlobalContextManager(contextManager); + + afterEach(() => { + exporter.forceFlush(); + }); + + after(async () => { + await provider.shutdown(); + disableContextAndManager(contextManager); + }); + + it('passing in null error or null span should have no effect', () => { + startTrace('aSpan', {opts: {tracerProvider: provider}}, span => { + const status1 = span.status; + let res = setSpanErrorAndException(span, null); + assert.strictEqual(res, false, 'nothing was set'); + const status2 = span.status; + assert.strictEqual( + status1, + status2, + 'setting null error should have no effect' + ); + + res = setSpanErrorAndException(null, null); + assert.strictEqual(res, false, 'nothing was set'); + }); + }); + + it('a non-empty string should set the message', () => { + startTrace('aSpan', {opts: {tracerProvider: provider}}, span => { + const status1 = span.status; + const res = setSpanErrorAndException(span, 'this one'); + assert.strictEqual(res, true, 'value was set'); + span.end(); + + const spans = exporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1, 'exactly 1 span must be exported'); + + const expSpan = spans[0]; + const status2 = expSpan.status; + assert.strictEqual(status2.message, 'this one'); + assert.strictEqual(status2.code, SpanStatusCode.ERROR); + + assert.strictEqual( + expSpan.events[0].attributes[SEMATTRS_EXCEPTION_MESSAGE], + 'this one', + 'the exception must have been recorded' + ); + }); + }); +}); diff --git a/package.json b/package.json index 723ff77b4..a8bc7432b 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,9 @@ "samples-test-with-archived": "cd samples/ && npm link ../ && npm test-with-archived && cd ../", "samples-test": "cd samples/ && npm link ../ && npm test && cd ../", "system-test": "mocha build/system-test --timeout 1600000", + "observability-test": "mocha build/observability-test --timeout 1600000", "cleanup": "mocha scripts/cleanup.js --timeout 30000", - "test": "mocha build/test build/test/common", + "test": "mocha build/test build/test/common build/observability-test", "ycsb": "node ./benchmark/ycsb.js run -P ./benchmark/workloada -p table=usertable -p cloudspanner.instance=ycsb-instance -p operationcount=100 -p cloudspanner.database=ycsb", "fix": "gts fix", "clean": "gts clean", @@ -42,6 +43,7 @@ "prepare": "npm run compile-protos && npm run compile", "pretest": "npm run compile", "presystem-test": "npm run compile", + "preobservability-test": "npm run compile", "proto": "compileProtos src", "docs-test": "linkinator docs", "predocs-test": "npm run docs", @@ -57,6 +59,9 @@ "@google-cloud/projectify": "^4.0.0", "@google-cloud/promisify": "^4.0.0", "@grpc/proto-loader": "^0.7.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.1", + "@opentelemetry/semantic-conventions": "^1.25.1", "@types/big.js": "^6.0.0", "@types/stack-trace": "0.0.33", "arrify": "^2.0.0", @@ -110,6 +115,7 @@ "mocha": "^9.2.2", "mv": "^2.1.1", "ncp": "^2.0.0", + "@opentelemetry/sdk-trace-node": "^1.25.1", "p-limit": "^3.0.1", "proxyquire": "^2.0.1", "sinon": "^18.0.0", diff --git a/protos/protos.d.ts b/protos/protos.d.ts index d6fb26a0d..c922b8d9f 100644 --- a/protos/protos.d.ts +++ b/protos/protos.d.ts @@ -18436,9 +18436,6 @@ export namespace google { /** ReadAction limit. */ public limit: number; - /** ReadAction _index. */ - public _index?: "index"; - /** * Creates a new ReadAction instance using the specified properties. * @param [properties] Properties to set @@ -18757,9 +18754,6 @@ export namespace google { /** DmlAction autocommitIfSupported. */ public autocommitIfSupported?: (boolean|null); - /** DmlAction _autocommitIfSupported. */ - public _autocommitIfSupported?: "autocommitIfSupported"; - /** * Creates a new DmlAction instance using the specified properties. * @param [properties] Properties to set @@ -19023,9 +19017,6 @@ export namespace google { /** Value valueType. */ public valueType?: ("isNull"|"intValue"|"boolValue"|"doubleValue"|"bytesValue"|"stringValue"|"structValue"|"timestampValue"|"dateDaysValue"|"isCommitTimestamp"|"arrayValue"); - /** Value _arrayType. */ - public _arrayType?: "arrayType"; - /** * Creates a new Value instance using the specified properties. * @param [properties] Properties to set @@ -19135,9 +19126,6 @@ export namespace google { /** KeyRange type. */ public type?: (google.spanner.executor.v1.KeyRange.Type|keyof typeof google.spanner.executor.v1.KeyRange.Type|null); - /** KeyRange _type. */ - public _type?: "type"; - /** * Creates a new KeyRange instance using the specified properties. * @param [properties] Properties to set @@ -20001,9 +19989,6 @@ export namespace google { /** PartitionedUpdateAction update. */ public update?: (google.spanner.executor.v1.IQueryAction|null); - /** PartitionedUpdateAction _options. */ - public _options?: "options"; - /** * Creates a new PartitionedUpdateAction instance using the specified properties. * @param [properties] Properties to set @@ -20109,12 +20094,6 @@ export namespace google { /** ExecutePartitionedUpdateOptions tag. */ public tag?: (string|null); - /** ExecutePartitionedUpdateOptions _rpcPriority. */ - public _rpcPriority?: "rpcPriority"; - - /** ExecutePartitionedUpdateOptions _tag. */ - public _tag?: "tag"; - /** * Creates a new ExecutePartitionedUpdateOptions instance using the specified properties. * @param [properties] Properties to set @@ -20231,12 +20210,6 @@ export namespace google { /** StartTransactionAction executionOptions. */ public executionOptions?: (google.spanner.executor.v1.ITransactionExecutionOptions|null); - /** StartTransactionAction _concurrency. */ - public _concurrency?: "concurrency"; - - /** StartTransactionAction _executionOptions. */ - public _executionOptions?: "executionOptions"; - /** * Creates a new StartTransactionAction instance using the specified properties. * @param [properties] Properties to set @@ -21293,9 +21266,6 @@ export namespace google { /** UpdateUserInstanceConfigAction labels. */ public labels: { [k: string]: string }; - /** UpdateUserInstanceConfigAction _displayName. */ - public _displayName?: "displayName"; - /** * Creates a new UpdateUserInstanceConfigAction instance using the specified properties. * @param [properties] Properties to set @@ -21611,12 +21581,6 @@ export namespace google { /** ListCloudInstanceConfigsAction pageToken. */ public pageToken?: (string|null); - /** ListCloudInstanceConfigsAction _pageSize. */ - public _pageSize?: "pageSize"; - - /** ListCloudInstanceConfigsAction _pageToken. */ - public _pageToken?: "pageToken"; - /** * Creates a new ListCloudInstanceConfigsAction instance using the specified properties. * @param [properties] Properties to set @@ -21750,15 +21714,6 @@ export namespace google { /** CreateCloudInstanceAction labels. */ public labels: { [k: string]: string }; - /** CreateCloudInstanceAction _nodeCount. */ - public _nodeCount?: "nodeCount"; - - /** CreateCloudInstanceAction _processingUnits. */ - public _processingUnits?: "processingUnits"; - - /** CreateCloudInstanceAction _autoscalingConfig. */ - public _autoscalingConfig?: "autoscalingConfig"; - /** * Creates a new CreateCloudInstanceAction instance using the specified properties. * @param [properties] Properties to set @@ -21892,18 +21847,6 @@ export namespace google { /** UpdateCloudInstanceAction labels. */ public labels: { [k: string]: string }; - /** UpdateCloudInstanceAction _displayName. */ - public _displayName?: "displayName"; - - /** UpdateCloudInstanceAction _nodeCount. */ - public _nodeCount?: "nodeCount"; - - /** UpdateCloudInstanceAction _processingUnits. */ - public _processingUnits?: "processingUnits"; - - /** UpdateCloudInstanceAction _autoscalingConfig. */ - public _autoscalingConfig?: "autoscalingConfig"; - /** * Creates a new UpdateCloudInstanceAction instance using the specified properties. * @param [properties] Properties to set @@ -22140,12 +22083,6 @@ export namespace google { /** CreateCloudDatabaseAction protoDescriptors. */ public protoDescriptors?: (Uint8Array|string|null); - /** CreateCloudDatabaseAction _dialect. */ - public _dialect?: "dialect"; - - /** CreateCloudDatabaseAction _protoDescriptors. */ - public _protoDescriptors?: "protoDescriptors"; - /** * Creates a new CreateCloudDatabaseAction instance using the specified properties. * @param [properties] Properties to set @@ -22273,9 +22210,6 @@ export namespace google { /** UpdateCloudDatabaseDdlAction protoDescriptors. */ public protoDescriptors?: (Uint8Array|string|null); - /** UpdateCloudDatabaseDdlAction _protoDescriptors. */ - public _protoDescriptors?: "protoDescriptors"; - /** * Creates a new UpdateCloudDatabaseDdlAction instance using the specified properties. * @param [properties] Properties to set @@ -22603,9 +22537,6 @@ export namespace google { /** ChangeQuorumCloudDatabaseAction servingLocations. */ public servingLocations: string[]; - /** ChangeQuorumCloudDatabaseAction _databaseUri. */ - public _databaseUri?: "databaseUri"; - /** * Creates a new ChangeQuorumCloudDatabaseAction instance using the specified properties. * @param [properties] Properties to set @@ -22836,15 +22767,6 @@ export namespace google { /** ListCloudInstancesAction pageToken. */ public pageToken?: (string|null); - /** ListCloudInstancesAction _filter. */ - public _filter?: "filter"; - - /** ListCloudInstancesAction _pageSize. */ - public _pageSize?: "pageSize"; - - /** ListCloudInstancesAction _pageToken. */ - public _pageToken?: "pageToken"; - /** * Creates a new ListCloudInstancesAction instance using the specified properties. * @param [properties] Properties to set @@ -23438,9 +23360,6 @@ export namespace google { /** CreateCloudBackupAction encryptionConfig. */ public encryptionConfig?: (google.spanner.admin.database.v1.IEncryptionConfig|null); - /** CreateCloudBackupAction _versionTime. */ - public _versionTime?: "versionTime"; - /** * Creates a new CreateCloudBackupAction instance using the specified properties. * @param [properties] Properties to set @@ -24758,12 +24677,6 @@ export namespace google { /** GenerateDbPartitionsForReadAction maxPartitionCount. */ public maxPartitionCount?: (number|Long|string|null); - /** GenerateDbPartitionsForReadAction _desiredBytesPerPartition. */ - public _desiredBytesPerPartition?: "desiredBytesPerPartition"; - - /** GenerateDbPartitionsForReadAction _maxPartitionCount. */ - public _maxPartitionCount?: "maxPartitionCount"; - /** * Creates a new GenerateDbPartitionsForReadAction instance using the specified properties. * @param [properties] Properties to set @@ -24867,9 +24780,6 @@ export namespace google { /** GenerateDbPartitionsForQueryAction desiredBytesPerPartition. */ public desiredBytesPerPartition?: (number|Long|string|null); - /** GenerateDbPartitionsForQueryAction _desiredBytesPerPartition. */ - public _desiredBytesPerPartition?: "desiredBytesPerPartition"; - /** * Creates a new GenerateDbPartitionsForQueryAction instance using the specified properties. * @param [properties] Properties to set @@ -24985,12 +24895,6 @@ export namespace google { /** BatchPartition index. */ public index?: (string|null); - /** BatchPartition _table. */ - public _table?: "table"; - - /** BatchPartition _index. */ - public _index?: "index"; - /** * Creates a new BatchPartition instance using the specified properties. * @param [properties] Properties to set @@ -25227,21 +25131,6 @@ export namespace google { /** ExecuteChangeStreamQuery cloudDatabaseRole. */ public cloudDatabaseRole?: (string|null); - /** ExecuteChangeStreamQuery _endTime. */ - public _endTime?: "endTime"; - - /** ExecuteChangeStreamQuery _partitionToken. */ - public _partitionToken?: "partitionToken"; - - /** ExecuteChangeStreamQuery _heartbeatMilliseconds. */ - public _heartbeatMilliseconds?: "heartbeatMilliseconds"; - - /** ExecuteChangeStreamQuery _deadlineSeconds. */ - public _deadlineSeconds?: "deadlineSeconds"; - - /** ExecuteChangeStreamQuery _cloudDatabaseRole. */ - public _cloudDatabaseRole?: "cloudDatabaseRole"; - /** * Creates a new ExecuteChangeStreamQuery instance using the specified properties. * @param [properties] Properties to set @@ -25393,27 +25282,6 @@ export namespace google { /** SpannerActionOutcome changeStreamRecords. */ public changeStreamRecords: google.spanner.executor.v1.IChangeStreamRecord[]; - /** SpannerActionOutcome _status. */ - public _status?: "status"; - - /** SpannerActionOutcome _commitTime. */ - public _commitTime?: "commitTime"; - - /** SpannerActionOutcome _readResult. */ - public _readResult?: "readResult"; - - /** SpannerActionOutcome _queryResult. */ - public _queryResult?: "queryResult"; - - /** SpannerActionOutcome _transactionRestarted. */ - public _transactionRestarted?: "transactionRestarted"; - - /** SpannerActionOutcome _batchTxnId. */ - public _batchTxnId?: "batchTxnId"; - - /** SpannerActionOutcome _adminResult. */ - public _adminResult?: "adminResult"; - /** * Creates a new SpannerActionOutcome instance using the specified properties. * @param [properties] Properties to set @@ -26213,15 +26081,6 @@ export namespace google { /** ReadResult rowType. */ public rowType?: (google.spanner.v1.IStructType|null); - /** ReadResult _index. */ - public _index?: "index"; - - /** ReadResult _requestIndex. */ - public _requestIndex?: "requestIndex"; - - /** ReadResult _rowType. */ - public _rowType?: "rowType"; - /** * Creates a new ReadResult instance using the specified properties. * @param [properties] Properties to set @@ -26325,9 +26184,6 @@ export namespace google { /** QueryResult rowType. */ public rowType?: (google.spanner.v1.IStructType|null); - /** QueryResult _rowType. */ - public _rowType?: "rowType"; - /** * Creates a new QueryResult instance using the specified properties. * @param [properties] Properties to set diff --git a/protos/protos.js b/protos/protos.js index 0d8508efa..46bf7d127 100644 --- a/protos/protos.js +++ b/protos/protos.js @@ -46636,12 +46636,7 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * ReadAction _index. - * @member {"index"|undefined} _index - * @memberof google.spanner.executor.v1.ReadAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ReadAction.prototype, "_index", { get: $util.oneOfGetter($oneOfFields = ["index"]), set: $util.oneOfSetter($oneOfFields) @@ -47453,12 +47448,7 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * DmlAction _autocommitIfSupported. - * @member {"autocommitIfSupported"|undefined} _autocommitIfSupported - * @memberof google.spanner.executor.v1.DmlAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(DmlAction.prototype, "_autocommitIfSupported", { get: $util.oneOfGetter($oneOfFields = ["autocommitIfSupported"]), set: $util.oneOfSetter($oneOfFields) @@ -48028,12 +48018,7 @@ set: $util.oneOfSetter($oneOfFields) }); - /** - * Value _arrayType. - * @member {"arrayType"|undefined} _arrayType - * @memberof google.spanner.executor.v1.Value - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(Value.prototype, "_arrayType", { get: $util.oneOfGetter($oneOfFields = ["arrayType"]), set: $util.oneOfSetter($oneOfFields) @@ -48522,12 +48507,7 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * KeyRange _type. - * @member {"type"|undefined} _type - * @memberof google.spanner.executor.v1.KeyRange - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(KeyRange.prototype, "_type", { get: $util.oneOfGetter($oneOfFields = ["type"]), set: $util.oneOfSetter($oneOfFields) @@ -50755,12 +50735,7 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * PartitionedUpdateAction _options. - * @member {"options"|undefined} _options - * @memberof google.spanner.executor.v1.PartitionedUpdateAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(PartitionedUpdateAction.prototype, "_options", { get: $util.oneOfGetter($oneOfFields = ["options"]), set: $util.oneOfSetter($oneOfFields) @@ -51008,23 +50983,13 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * ExecutePartitionedUpdateOptions _rpcPriority. - * @member {"rpcPriority"|undefined} _rpcPriority - * @memberof google.spanner.executor.v1.PartitionedUpdateAction.ExecutePartitionedUpdateOptions - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ExecutePartitionedUpdateOptions.prototype, "_rpcPriority", { get: $util.oneOfGetter($oneOfFields = ["rpcPriority"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * ExecutePartitionedUpdateOptions _tag. - * @member {"tag"|undefined} _tag - * @memberof google.spanner.executor.v1.PartitionedUpdateAction.ExecutePartitionedUpdateOptions - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ExecutePartitionedUpdateOptions.prototype, "_tag", { get: $util.oneOfGetter($oneOfFields = ["tag"]), set: $util.oneOfSetter($oneOfFields) @@ -51318,23 +51283,13 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * StartTransactionAction _concurrency. - * @member {"concurrency"|undefined} _concurrency - * @memberof google.spanner.executor.v1.StartTransactionAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(StartTransactionAction.prototype, "_concurrency", { get: $util.oneOfGetter($oneOfFields = ["concurrency"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * StartTransactionAction _executionOptions. - * @member {"executionOptions"|undefined} _executionOptions - * @memberof google.spanner.executor.v1.StartTransactionAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(StartTransactionAction.prototype, "_executionOptions", { get: $util.oneOfGetter($oneOfFields = ["executionOptions"]), set: $util.oneOfSetter($oneOfFields) @@ -54567,12 +54522,7 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * UpdateUserInstanceConfigAction _displayName. - * @member {"displayName"|undefined} _displayName - * @memberof google.spanner.executor.v1.UpdateUserInstanceConfigAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(UpdateUserInstanceConfigAction.prototype, "_displayName", { get: $util.oneOfGetter($oneOfFields = ["displayName"]), set: $util.oneOfSetter($oneOfFields) @@ -55339,23 +55289,13 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * ListCloudInstanceConfigsAction _pageSize. - * @member {"pageSize"|undefined} _pageSize - * @memberof google.spanner.executor.v1.ListCloudInstanceConfigsAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ListCloudInstanceConfigsAction.prototype, "_pageSize", { get: $util.oneOfGetter($oneOfFields = ["pageSize"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * ListCloudInstanceConfigsAction _pageToken. - * @member {"pageToken"|undefined} _pageToken - * @memberof google.spanner.executor.v1.ListCloudInstanceConfigsAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ListCloudInstanceConfigsAction.prototype, "_pageToken", { get: $util.oneOfGetter($oneOfFields = ["pageToken"]), set: $util.oneOfSetter($oneOfFields) @@ -55659,34 +55599,19 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * CreateCloudInstanceAction _nodeCount. - * @member {"nodeCount"|undefined} _nodeCount - * @memberof google.spanner.executor.v1.CreateCloudInstanceAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(CreateCloudInstanceAction.prototype, "_nodeCount", { get: $util.oneOfGetter($oneOfFields = ["nodeCount"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * CreateCloudInstanceAction _processingUnits. - * @member {"processingUnits"|undefined} _processingUnits - * @memberof google.spanner.executor.v1.CreateCloudInstanceAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(CreateCloudInstanceAction.prototype, "_processingUnits", { get: $util.oneOfGetter($oneOfFields = ["processingUnits"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * CreateCloudInstanceAction _autoscalingConfig. - * @member {"autoscalingConfig"|undefined} _autoscalingConfig - * @memberof google.spanner.executor.v1.CreateCloudInstanceAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(CreateCloudInstanceAction.prototype, "_autoscalingConfig", { get: $util.oneOfGetter($oneOfFields = ["autoscalingConfig"]), set: $util.oneOfSetter($oneOfFields) @@ -56092,45 +56017,25 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * UpdateCloudInstanceAction _displayName. - * @member {"displayName"|undefined} _displayName - * @memberof google.spanner.executor.v1.UpdateCloudInstanceAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(UpdateCloudInstanceAction.prototype, "_displayName", { get: $util.oneOfGetter($oneOfFields = ["displayName"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * UpdateCloudInstanceAction _nodeCount. - * @member {"nodeCount"|undefined} _nodeCount - * @memberof google.spanner.executor.v1.UpdateCloudInstanceAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(UpdateCloudInstanceAction.prototype, "_nodeCount", { get: $util.oneOfGetter($oneOfFields = ["nodeCount"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * UpdateCloudInstanceAction _processingUnits. - * @member {"processingUnits"|undefined} _processingUnits - * @memberof google.spanner.executor.v1.UpdateCloudInstanceAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(UpdateCloudInstanceAction.prototype, "_processingUnits", { get: $util.oneOfGetter($oneOfFields = ["processingUnits"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * UpdateCloudInstanceAction _autoscalingConfig. - * @member {"autoscalingConfig"|undefined} _autoscalingConfig - * @memberof google.spanner.executor.v1.UpdateCloudInstanceAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(UpdateCloudInstanceAction.prototype, "_autoscalingConfig", { get: $util.oneOfGetter($oneOfFields = ["autoscalingConfig"]), set: $util.oneOfSetter($oneOfFields) @@ -56767,23 +56672,13 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * CreateCloudDatabaseAction _dialect. - * @member {"dialect"|undefined} _dialect - * @memberof google.spanner.executor.v1.CreateCloudDatabaseAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(CreateCloudDatabaseAction.prototype, "_dialect", { get: $util.oneOfGetter($oneOfFields = ["dialect"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * CreateCloudDatabaseAction _protoDescriptors. - * @member {"protoDescriptors"|undefined} _protoDescriptors - * @memberof google.spanner.executor.v1.CreateCloudDatabaseAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(CreateCloudDatabaseAction.prototype, "_protoDescriptors", { get: $util.oneOfGetter($oneOfFields = ["protoDescriptors"]), set: $util.oneOfSetter($oneOfFields) @@ -57159,12 +57054,7 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * UpdateCloudDatabaseDdlAction _protoDescriptors. - * @member {"protoDescriptors"|undefined} _protoDescriptors - * @memberof google.spanner.executor.v1.UpdateCloudDatabaseDdlAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(UpdateCloudDatabaseDdlAction.prototype, "_protoDescriptors", { get: $util.oneOfGetter($oneOfFields = ["protoDescriptors"]), set: $util.oneOfSetter($oneOfFields) @@ -58004,12 +57894,7 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * ChangeQuorumCloudDatabaseAction _databaseUri. - * @member {"databaseUri"|undefined} _databaseUri - * @memberof google.spanner.executor.v1.ChangeQuorumCloudDatabaseAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ChangeQuorumCloudDatabaseAction.prototype, "_databaseUri", { get: $util.oneOfGetter($oneOfFields = ["databaseUri"]), set: $util.oneOfSetter($oneOfFields) @@ -58555,34 +58440,19 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * ListCloudInstancesAction _filter. - * @member {"filter"|undefined} _filter - * @memberof google.spanner.executor.v1.ListCloudInstancesAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ListCloudInstancesAction.prototype, "_filter", { get: $util.oneOfGetter($oneOfFields = ["filter"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * ListCloudInstancesAction _pageSize. - * @member {"pageSize"|undefined} _pageSize - * @memberof google.spanner.executor.v1.ListCloudInstancesAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ListCloudInstancesAction.prototype, "_pageSize", { get: $util.oneOfGetter($oneOfFields = ["pageSize"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * ListCloudInstancesAction _pageToken. - * @member {"pageToken"|undefined} _pageToken - * @memberof google.spanner.executor.v1.ListCloudInstancesAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ListCloudInstancesAction.prototype, "_pageToken", { get: $util.oneOfGetter($oneOfFields = ["pageToken"]), set: $util.oneOfSetter($oneOfFields) @@ -60000,12 +59870,7 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * CreateCloudBackupAction _versionTime. - * @member {"versionTime"|undefined} _versionTime - * @memberof google.spanner.executor.v1.CreateCloudBackupAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(CreateCloudBackupAction.prototype, "_versionTime", { get: $util.oneOfGetter($oneOfFields = ["versionTime"]), set: $util.oneOfSetter($oneOfFields) @@ -63141,23 +63006,13 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * GenerateDbPartitionsForReadAction _desiredBytesPerPartition. - * @member {"desiredBytesPerPartition"|undefined} _desiredBytesPerPartition - * @memberof google.spanner.executor.v1.GenerateDbPartitionsForReadAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(GenerateDbPartitionsForReadAction.prototype, "_desiredBytesPerPartition", { get: $util.oneOfGetter($oneOfFields = ["desiredBytesPerPartition"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * GenerateDbPartitionsForReadAction _maxPartitionCount. - * @member {"maxPartitionCount"|undefined} _maxPartitionCount - * @memberof google.spanner.executor.v1.GenerateDbPartitionsForReadAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(GenerateDbPartitionsForReadAction.prototype, "_maxPartitionCount", { get: $util.oneOfGetter($oneOfFields = ["maxPartitionCount"]), set: $util.oneOfSetter($oneOfFields) @@ -63475,12 +63330,7 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * GenerateDbPartitionsForQueryAction _desiredBytesPerPartition. - * @member {"desiredBytesPerPartition"|undefined} _desiredBytesPerPartition - * @memberof google.spanner.executor.v1.GenerateDbPartitionsForQueryAction - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(GenerateDbPartitionsForQueryAction.prototype, "_desiredBytesPerPartition", { get: $util.oneOfGetter($oneOfFields = ["desiredBytesPerPartition"]), set: $util.oneOfSetter($oneOfFields) @@ -63753,23 +63603,13 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * BatchPartition _table. - * @member {"table"|undefined} _table - * @memberof google.spanner.executor.v1.BatchPartition - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(BatchPartition.prototype, "_table", { get: $util.oneOfGetter($oneOfFields = ["table"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * BatchPartition _index. - * @member {"index"|undefined} _index - * @memberof google.spanner.executor.v1.BatchPartition - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(BatchPartition.prototype, "_index", { get: $util.oneOfGetter($oneOfFields = ["index"]), set: $util.oneOfSetter($oneOfFields) @@ -64323,56 +64163,31 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * ExecuteChangeStreamQuery _endTime. - * @member {"endTime"|undefined} _endTime - * @memberof google.spanner.executor.v1.ExecuteChangeStreamQuery - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ExecuteChangeStreamQuery.prototype, "_endTime", { get: $util.oneOfGetter($oneOfFields = ["endTime"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * ExecuteChangeStreamQuery _partitionToken. - * @member {"partitionToken"|undefined} _partitionToken - * @memberof google.spanner.executor.v1.ExecuteChangeStreamQuery - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ExecuteChangeStreamQuery.prototype, "_partitionToken", { get: $util.oneOfGetter($oneOfFields = ["partitionToken"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * ExecuteChangeStreamQuery _heartbeatMilliseconds. - * @member {"heartbeatMilliseconds"|undefined} _heartbeatMilliseconds - * @memberof google.spanner.executor.v1.ExecuteChangeStreamQuery - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ExecuteChangeStreamQuery.prototype, "_heartbeatMilliseconds", { get: $util.oneOfGetter($oneOfFields = ["heartbeatMilliseconds"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * ExecuteChangeStreamQuery _deadlineSeconds. - * @member {"deadlineSeconds"|undefined} _deadlineSeconds - * @memberof google.spanner.executor.v1.ExecuteChangeStreamQuery - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ExecuteChangeStreamQuery.prototype, "_deadlineSeconds", { get: $util.oneOfGetter($oneOfFields = ["deadlineSeconds"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * ExecuteChangeStreamQuery _cloudDatabaseRole. - * @member {"cloudDatabaseRole"|undefined} _cloudDatabaseRole - * @memberof google.spanner.executor.v1.ExecuteChangeStreamQuery - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ExecuteChangeStreamQuery.prototype, "_cloudDatabaseRole", { get: $util.oneOfGetter($oneOfFields = ["cloudDatabaseRole"]), set: $util.oneOfSetter($oneOfFields) @@ -64825,78 +64640,43 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * SpannerActionOutcome _status. - * @member {"status"|undefined} _status - * @memberof google.spanner.executor.v1.SpannerActionOutcome - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(SpannerActionOutcome.prototype, "_status", { get: $util.oneOfGetter($oneOfFields = ["status"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * SpannerActionOutcome _commitTime. - * @member {"commitTime"|undefined} _commitTime - * @memberof google.spanner.executor.v1.SpannerActionOutcome - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(SpannerActionOutcome.prototype, "_commitTime", { get: $util.oneOfGetter($oneOfFields = ["commitTime"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * SpannerActionOutcome _readResult. - * @member {"readResult"|undefined} _readResult - * @memberof google.spanner.executor.v1.SpannerActionOutcome - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(SpannerActionOutcome.prototype, "_readResult", { get: $util.oneOfGetter($oneOfFields = ["readResult"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * SpannerActionOutcome _queryResult. - * @member {"queryResult"|undefined} _queryResult - * @memberof google.spanner.executor.v1.SpannerActionOutcome - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(SpannerActionOutcome.prototype, "_queryResult", { get: $util.oneOfGetter($oneOfFields = ["queryResult"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * SpannerActionOutcome _transactionRestarted. - * @member {"transactionRestarted"|undefined} _transactionRestarted - * @memberof google.spanner.executor.v1.SpannerActionOutcome - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(SpannerActionOutcome.prototype, "_transactionRestarted", { get: $util.oneOfGetter($oneOfFields = ["transactionRestarted"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * SpannerActionOutcome _batchTxnId. - * @member {"batchTxnId"|undefined} _batchTxnId - * @memberof google.spanner.executor.v1.SpannerActionOutcome - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(SpannerActionOutcome.prototype, "_batchTxnId", { get: $util.oneOfGetter($oneOfFields = ["batchTxnId"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * SpannerActionOutcome _adminResult. - * @member {"adminResult"|undefined} _adminResult - * @memberof google.spanner.executor.v1.SpannerActionOutcome - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(SpannerActionOutcome.prototype, "_adminResult", { get: $util.oneOfGetter($oneOfFields = ["adminResult"]), set: $util.oneOfSetter($oneOfFields) @@ -67203,34 +66983,19 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * ReadResult _index. - * @member {"index"|undefined} _index - * @memberof google.spanner.executor.v1.ReadResult - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ReadResult.prototype, "_index", { get: $util.oneOfGetter($oneOfFields = ["index"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * ReadResult _requestIndex. - * @member {"requestIndex"|undefined} _requestIndex - * @memberof google.spanner.executor.v1.ReadResult - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ReadResult.prototype, "_requestIndex", { get: $util.oneOfGetter($oneOfFields = ["requestIndex"]), set: $util.oneOfSetter($oneOfFields) }); - /** - * ReadResult _rowType. - * @member {"rowType"|undefined} _rowType - * @memberof google.spanner.executor.v1.ReadResult - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(ReadResult.prototype, "_rowType", { get: $util.oneOfGetter($oneOfFields = ["rowType"]), set: $util.oneOfSetter($oneOfFields) @@ -67548,12 +67313,7 @@ // OneOf field names bound to virtual getters and setters var $oneOfFields; - /** - * QueryResult _rowType. - * @member {"rowType"|undefined} _rowType - * @memberof google.spanner.executor.v1.QueryResult - * @instance - */ + // Virtual OneOf for proto3 optional field Object.defineProperty(QueryResult.prototype, "_rowType", { get: $util.oneOfGetter($oneOfFields = ["rowType"]), set: $util.oneOfSetter($oneOfFields) diff --git a/protos/protos.json b/protos/protos.json index 3b2ab56bc..68b8708a8 100644 --- a/protos/protos.json +++ b/protos/protos.json @@ -1,4 +1,7 @@ { + "options": { + "syntax": "proto3" + }, "nested": { "google": { "nested": { diff --git a/src/instrument.ts b/src/instrument.ts new file mode 100644 index 000000000..a242c4ed2 --- /dev/null +++ b/src/instrument.ts @@ -0,0 +1,242 @@ +/*! + * Copyright 2024 Google LLC. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + SEMATTRS_DB_NAME, + SEMATTRS_DB_STATEMENT, + SEMATTRS_DB_SYSTEM, + SEMATTRS_DB_SQL_TABLE, +} from '@opentelemetry/semantic-conventions'; + +import { + Span, + SpanStatusCode, + trace, + INVALID_SPAN_CONTEXT, + SpanAttributes, + TimeInput, + TracerProvider, + Link, + Exception, + SpanContext, + SpanStatus, + SpanKind, +} from '@opentelemetry/api'; + +const optedInPII: boolean = + process.env.SPANNER_ENABLE_EXTENDED_TRACING === 'true'; + +interface SQLStatement { + sql: string; +} + +interface observabilityOptions { + tracerProvider: TracerProvider; + enableExtendedTracing: boolean; +} + +export type {observabilityOptions as ObservabilityOptions}; + +const TRACER_NAME = 'cloud.google.com/nodejs/spanner'; +const TRACER_VERSION = '7.14.0'; // Manually hard coded, TODO: remove + +/** + * getTracer fetches the tracer from the provided tracerProvider. + * @param {TracerProvider} [tracerProvider] optional custom tracer provider + * to use for fetching the tracer. If not provided, the global provider will be used. + * + * @returns {Tracer} The tracer instance associated with the provided or global provider. + */ +export function getTracer(tracerProvider?: TracerProvider) { + if (tracerProvider) { + return tracerProvider.getTracer(TRACER_NAME, TRACER_VERSION); + } + // Otherwise use the global tracer. + return trace.getTracer(TRACER_NAME, TRACER_VERSION); +} + +interface traceConfig { + sql?: string | SQLStatement; + tableName?: string; + dbName?: string; + opts?: observabilityOptions; + that?: Object; +} + +const SPAN_NAMESPACE_PREFIX = 'CloudSpanner'; // TODO: discuss & standardize this prefix. +export {SPAN_NAMESPACE_PREFIX}; + +/** + * startTrace begins an active span in the current active context + * and passes it back to the set callback function. Each span will + * be prefixed with "cloud.google.com/nodejs/spanner". It is the + * responsibility of the caller to invoke [span.end] when finished tracing. + * + * @returns {Span} The created span. + */ +export function startTrace( + spanNameSuffix: string, + config: traceConfig | undefined, + cb: (span: Span) => T +): T { + const origConfig = config; + if (!config) { + config = {} as traceConfig; + } + + return getTracer(config.opts?.tracerProvider).startActiveSpan( + SPAN_NAMESPACE_PREFIX + '.' + spanNameSuffix, + {kind: SpanKind.CLIENT}, + span => { + span.setAttribute(SEMATTRS_DB_SYSTEM, 'spanner'); + + if (config.tableName) { + span.setAttribute(SEMATTRS_DB_SQL_TABLE, config.tableName); + } + if (config.dbName) { + span.setAttribute(SEMATTRS_DB_NAME, config.dbName); + } + + const allowExtendedTracing = + optedInPII || config.opts?.enableExtendedTracing; + if (config.sql && allowExtendedTracing) { + const sql = config.sql; + if (typeof sql === 'string') { + span.setAttribute(SEMATTRS_DB_STATEMENT, sql as string); + } else { + const stmt = sql as SQLStatement; + span.setAttribute(SEMATTRS_DB_STATEMENT, stmt.sql); + } + } + + if (config.that) { + const fn = cb.bind(config.that); + return fn(span); + } else { + return cb(span); + } + } + ); +} + +/** + * Sets the span status with err, if non-null onto the span with + * status.code=ERROR and the message of err.toString() + * + * @returns {boolean} to signify if the status was set. + */ +export function setSpanError(span: Span, err: Error | String): boolean { + if (!err || !span) { + return false; + } + + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.toString(), + }); + return true; +} + +/** + * Sets err, if non-null onto the span with + * status.code=ERROR and the message of err.toString() + * as well as recording an exception on the span. + * @param {Span} [span] the subject span + * @param {Error} [err] the error whose message to use to record + * the span error and the span exception. + * + * @returns {boolean} to signify if the status and exception were set. + */ +export function setSpanErrorAndException( + span: Span, + err: Error | String +): boolean { + if (setSpanError(span, err)) { + span.recordException(err as Error); + return true; + } + return false; +} + +/** + * getActiveOrNoopSpan queries the global tracer for the currently active + * span and returns it, otherwise if there is no active span available, it'll + * simply create a NoopSpan. This is important in the cases where we don't + * want to create a new span, such as in sensitive and frequently called code + * for which the new spans would be too many and thus pollute the trace, + * but yet we'd like to record an important annotation. + * + * @returns {Span} the non-null span. + */ +export function getActiveOrNoopSpan(): Span { + const span = trace.getActiveSpan(); + if (span) { + return span; + } + return new noopSpan(); +} + +/** + * noopSpan is a pass-through Span that does nothing and shall not + * be exported, nor added into any context. It serves as a placeholder + * to allow calls in sensitive areas like sessionPools to transparently + * add attributes to spans without lots of ugly null checks. + * + * It exists because OpenTelemetry-JS does not seem to export the NoopSpan. + */ +class noopSpan implements Span { + constructor() {} + + spanContext(): SpanContext { + return INVALID_SPAN_CONTEXT; + } + + setAttribute(key: string, value: unknown): this { + return this; + } + + setAttributes(attributes: SpanAttributes): this { + return this; + } + + addEvent(name: string, attributes?: SpanAttributes): this { + return this; + } + + addLink(link: Link): this { + return this; + } + + addLinks(links: Link[]): this { + return this; + } + + setStatus(status: SpanStatus): this { + return this; + } + + end(endTime?: TimeInput): void {} + + isRecording(): boolean { + return false; + } + + recordException(exc: Exception, timeAt?: TimeInput): void {} + + updateName(name: string): this { + return this; + } +} diff --git a/tsconfig.json b/tsconfig.json index 3824d3ff5..9b165b9e7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,7 @@ "test/*.ts", "test/**/*.ts", "system-test/*.ts", - "benchmark/*.ts" + "benchmark/*.ts", + "observability-test/*.ts" ] }