Skip to content

Commit 207b382

Browse files
authored
Merge branch 'googleapis:main' into main
2 parents 4b2f196 + f08cf4b commit 207b382

20 files changed

+539
-675
lines changed

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,30 @@
44

55
[1]: https://www.npmjs.com/package/nodejs-spanner?activeTab=versions
66

7+
## [7.15.0](https://github.com/googleapis/nodejs-spanner/compare/v7.14.0...v7.15.0) (2024-10-30)
8+
9+
10+
### Features
11+
12+
* (observability, samples): add tracing end-to-end sample ([#2130](https://github.com/googleapis/nodejs-spanner/issues/2130)) ([66d99e8](https://github.com/googleapis/nodejs-spanner/commit/66d99e836cd2bfbb3b0f78980ec2b499f9e5e563))
13+
* (observability) add spans for BatchTransaction and Table ([#2115](https://github.com/googleapis/nodejs-spanner/issues/2115)) ([d51aae9](https://github.com/googleapis/nodejs-spanner/commit/d51aae9c9c3c0e6319d81c2809573ae54675acf3)), closes [#2114](https://github.com/googleapis/nodejs-spanner/issues/2114)
14+
* (observability) Add support for OpenTelemetry traces and allow observability options to be passed. ([#2131](https://github.com/googleapis/nodejs-spanner/issues/2131)) ([5237e11](https://github.com/googleapis/nodejs-spanner/commit/5237e118befb4b7fe4aea76a80a91e822d7a22e4)), closes [#2079](https://github.com/googleapis/nodejs-spanner/issues/2079)
15+
* (observability) propagate database name for every span generated to aid in quick debugging ([#2155](https://github.com/googleapis/nodejs-spanner/issues/2155)) ([0342e74](https://github.com/googleapis/nodejs-spanner/commit/0342e74721a0684d8195a6299c3a634eefc2b522))
16+
* (observability) trace Database.batchCreateSessions + SessionPool.createSessions ([#2145](https://github.com/googleapis/nodejs-spanner/issues/2145)) ([f489c94](https://github.com/googleapis/nodejs-spanner/commit/f489c9479fa5402f0c960cf896fd3be0e946f182))
17+
* (observability): trace Database.runPartitionedUpdate ([#2176](https://github.com/googleapis/nodejs-spanner/issues/2176)) ([701e226](https://github.com/googleapis/nodejs-spanner/commit/701e22660d5ac9f0b3e940ad656b9ca6c479251d)), closes [#2079](https://github.com/googleapis/nodejs-spanner/issues/2079)
18+
* (observability): trace Database.runTransactionAsync ([#2167](https://github.com/googleapis/nodejs-spanner/issues/2167)) ([d0fe178](https://github.com/googleapis/nodejs-spanner/commit/d0fe178623c1c48245d11bcea97fcd340b6615af)), closes [#207](https://github.com/googleapis/nodejs-spanner/issues/207)
19+
* Allow multiple KMS keys to create CMEK database/backup ([#2099](https://github.com/googleapis/nodejs-spanner/issues/2099)) ([51bc8a7](https://github.com/googleapis/nodejs-spanner/commit/51bc8a7445ab8b3d2239493b69d9c271c1086dde))
20+
* **observability:** Fix bugs found from product review + negative cases ([#2158](https://github.com/googleapis/nodejs-spanner/issues/2158)) ([cbc86fa](https://github.com/googleapis/nodejs-spanner/commit/cbc86fa80498af6bd745eebb9443612936e26d4e))
21+
* **observability:** Trace Database methods ([#2119](https://github.com/googleapis/nodejs-spanner/issues/2119)) ([1f06871](https://github.com/googleapis/nodejs-spanner/commit/1f06871f7aca386756e8691013602b069697bb87)), closes [#2114](https://github.com/googleapis/nodejs-spanner/issues/2114)
22+
* **observability:** Trace Database.batchWriteAtLeastOnce ([#2157](https://github.com/googleapis/nodejs-spanner/issues/2157)) ([2a19ef1](https://github.com/googleapis/nodejs-spanner/commit/2a19ef1af4f6fd1b81d08afc15db76007859a0b9)), closes [#2079](https://github.com/googleapis/nodejs-spanner/issues/2079)
23+
* **observability:** Trace Transaction ([#2122](https://github.com/googleapis/nodejs-spanner/issues/2122)) ([a464bdb](https://github.com/googleapis/nodejs-spanner/commit/a464bdb5cbb7856b7a08dac3ff48132948b65792)), closes [#2114](https://github.com/googleapis/nodejs-spanner/issues/2114)
24+
25+
26+
### Bug Fixes
27+
28+
* Exact staleness timebound ([#2143](https://github.com/googleapis/nodejs-spanner/issues/2143)) ([f01516e](https://github.com/googleapis/nodejs-spanner/commit/f01516ec6ba44730622cfb050c52cd93f30bba7a)), closes [#2129](https://github.com/googleapis/nodejs-spanner/issues/2129)
29+
* GetMetadata for Session ([#2124](https://github.com/googleapis/nodejs-spanner/issues/2124)) ([2fd63ac](https://github.com/googleapis/nodejs-spanner/commit/2fd63acb87ce06a02d7fdfa78d836dbd7ad59a26)), closes [#2123](https://github.com/googleapis/nodejs-spanner/issues/2123)
30+
731
## [7.14.0](https://github.com/googleapis/nodejs-spanner/compare/v7.13.0...v7.14.0) (2024-08-14)
832

933

OBSERVABILITY.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
## Observability with OpenTelemetry
2+
3+
This Cloud Spanner client supports [OpenTelemetry Traces](https://opentelemetry.io/), which gives insight into the client internals and aids in debugging/troubleshooting production issues.
4+
5+
By default, the functionality is disabled. You shall need to add OpenTelemetry dependencies, and must configure and
6+
enable OpenTelemetry with appropriate exporters at the startup of your application:
7+
8+
**Table of contents:**
9+
10+
* [Observability](#observability)
11+
* [Tracing](#tracing)
12+
* [OpenTelemetry Dependencies](#opentelemetry-dependencies)
13+
* [OpenTelemetry Configuration](#opentelemetry-configuration)
14+
* [SQL Statement span annotation](#sql-statement-span-annotation)
15+
* [OpenTelemetry gRCP instrumentation](#opentelemetry-grpc-instrumentation)
16+
* [Tracing Sample](#tracing-sample)
17+
18+
### Tracing
19+
20+
#### OpenTelemetry Dependencies
21+
22+
Add the following dependencies in your `package.json` or install them directly.
23+
```javascript
24+
// Required packages for OpenTelemetry SDKs
25+
"@opentelemetry/sdk-trace-base": "^1.26.0",
26+
"@opentelemetry/sdk-trace-node": "^1.26.0",
27+
28+
// Package to use Google Cloud Trace exporter
29+
"@google-cloud/opentelemetry-cloud-trace-exporter": "^2.4.1",
30+
31+
// Packages to enable gRPC instrumentation
32+
"@opentelemetry/instrumentation": "^0.53.0",
33+
"@opentelemetry/instrumentation-grpc": "^0.53.0",
34+
```
35+
36+
#### OpenTelemetry Configuration
37+
38+
```javascript
39+
const {
40+
NodeTracerProvider,
41+
TraceIdRatioBasedSampler,
42+
} = require('@opentelemetry/sdk-trace-node');
43+
const {
44+
BatchSpanProcessor,
45+
} = require('@opentelemetry/sdk-trace-base');
46+
const {
47+
TraceExporter,
48+
} = require('@google-cloud/opentelemetry-cloud-trace-exporter');
49+
const exporter = new TraceExporter();
50+
51+
// Create the tracerProvider that the exporter shall be attached to.
52+
const provider = new NodeTracerProvider({resource: resource});
53+
provider.addSpanProcessor(new BatchSpanProcessor(exporter));
54+
55+
// Create the Cloud Spanner Client.
56+
const {Spanner} = require('@google-cloud/spanner');
57+
const spanner = new Spanner({
58+
projectId: projectId,
59+
observabilityOptions: {
60+
// Inject the TracerProvider via SpannerOptions or
61+
// register it as a global by invoking `provider.register()`
62+
tracerProvider: provider,
63+
},
64+
});
65+
```
66+
67+
#### SQL Statement span annotation
68+
69+
To allow your SQL statements to be annotated in the appropriate spans, you need to opt-in, because
70+
SQL statements can contain sensitive personally-identifiable-information (PII).
71+
72+
You can opt-in by either:
73+
74+
* Setting the environment variable `SPANNER_ENABLE_EXTENDED_TRACING=true` before your application is started
75+
* In code, setting `enableExtendedTracing: true` in your SpannerOptions before creating the Cloud Spanner client
76+
77+
```javascript
78+
const spanner = new Spanner({
79+
projectId: projectId,
80+
observabilityOptions: {
81+
tracerProvider: provider,
82+
enableExtendedTracing: true,
83+
},
84+
```
85+
86+
#### OpenTelemetry gRPC instrumentation
87+
88+
Optionally, you can enable OpenTelemetry gRPC instrumentation which produces traces of executed remote procedure calls (RPCs)
89+
in your programs by these imports and instantiation. You could pass in the traceProvider or register it globally
90+
by invoking `tracerProvider.register()`
91+
92+
```javascript
93+
const {registerInstrumentations} = require('@opentelemetry/instrumentation');
94+
const {GrpcInstrumentation} = require('@opentelemetry/instrumentation-grpc');
95+
registerInstrumentations({
96+
tracerProvider: tracerProvider,
97+
instrumentations: [new GrpcInstrumentation()],
98+
});
99+
```
100+
101+
#### Tracing Sample
102+
For more information please see this [sample code](./samples/observability-traces.js)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/nodejs-spanner/tre
155155
| Numeric-add-column | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/numeric-add-column.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/numeric-add-column.js,samples/README.md) |
156156
| Numeric-query-parameter | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/numeric-query-parameter.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/numeric-query-parameter.js,samples/README.md) |
157157
| Numeric-update-data | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/numeric-update-data.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/numeric-update-data.js,samples/README.md) |
158+
| Observability (Tracing) with OpenTelemetry | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/observability-traces.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/observability-traces.js,samples/README.md) |
158159
| Adds a column to an existing table in a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-add-column.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-add-column.js,samples/README.md) |
159160
| Showcase the rules for case-sensitivity and case folding for a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-case-sensitivity.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-case-sensitivity.js,samples/README.md) |
160161
| Creates a PostgreSQL Database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-database-create.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-database-create.js,samples/README.md) |

observability-test/batch-transaction.ts

Lines changed: 83 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -153,103 +153,95 @@ describe('BatchTransaction', () => {
153153
};
154154

155155
it('createQueryPartitions', done => {
156-
const REQUEST = sandbox.stub();
157-
158-
const res = batchTransaction.createQueryPartitions(
159-
QUERY,
160-
(err, part, resp) => {
161-
assert.ifError(err);
162-
traceExporter.forceFlush();
163-
const spans = traceExporter.getFinishedSpans();
164-
assert.strictEqual(spans.length, 2, 'Exactly 2 spans expected');
165-
166-
// Sort the spans by duration.
167-
spans.sort((spanA, spanB) => {
168-
spanA.duration < spanB.duration;
169-
});
170-
171-
const actualSpanNames: string[] = [];
172-
spans.forEach(span => {
173-
actualSpanNames.push(span.name);
174-
});
175-
176-
const expectedSpanNames = [
177-
'CloudSpanner.BatchTransaction.createPartitions_',
178-
'CloudSpanner.BatchTransaction.createQueryPartitions',
179-
];
180-
assert.deepStrictEqual(
181-
actualSpanNames,
182-
expectedSpanNames,
183-
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
184-
);
185-
186-
// Ensure that createPartitions_ is a child span of createQueryPartitions.
187-
const spanCreatePartitions_ = spans[0];
188-
const spanCreateQueryPartitions = spans[1];
189-
assert.ok(
190-
spanCreateQueryPartitions.spanContext().traceId,
191-
'Expected that createQueryPartitions has a defined traceId'
192-
);
193-
assert.ok(
194-
spanCreatePartitions_.spanContext().traceId,
195-
'Expected that createPartitions_ has a defined traceId'
196-
);
197-
assert.deepStrictEqual(
198-
spanCreatePartitions_.spanContext().traceId,
199-
spanCreateQueryPartitions.spanContext().traceId,
200-
'Expected that both spans share a traceId'
201-
);
202-
assert.ok(
203-
spanCreateQueryPartitions.spanContext().spanId,
204-
'Expected that createQueryPartitions has a defined spanId'
205-
);
206-
assert.ok(
207-
spanCreatePartitions_.spanContext().spanId,
208-
'Expected that createPartitions_ has a defined spanId'
209-
);
210-
assert.deepStrictEqual(
211-
spanCreatePartitions_.parentSpanId,
212-
spanCreateQueryPartitions.spanContext().spanId,
213-
'Expected that createQueryPartitions is the parent to createPartitions_'
214-
);
215-
done();
216-
}
217-
);
156+
batchTransaction.createQueryPartitions(QUERY, err => {
157+
assert.ifError(err);
158+
traceExporter.forceFlush();
159+
const spans = traceExporter.getFinishedSpans();
160+
assert.strictEqual(spans.length, 2, 'Exactly 2 spans expected');
161+
162+
// Sort the spans by duration.
163+
spans.sort((spanA, spanB) => {
164+
spanA.duration < spanB.duration;
165+
});
166+
167+
const actualSpanNames: string[] = [];
168+
spans.forEach(span => {
169+
actualSpanNames.push(span.name);
170+
});
171+
172+
const expectedSpanNames = [
173+
'CloudSpanner.BatchTransaction.createPartitions_',
174+
'CloudSpanner.BatchTransaction.createQueryPartitions',
175+
];
176+
assert.deepStrictEqual(
177+
actualSpanNames,
178+
expectedSpanNames,
179+
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
180+
);
181+
182+
// Ensure that createPartitions_ is a child span of createQueryPartitions.
183+
const spanCreatePartitions_ = spans[0];
184+
const spanCreateQueryPartitions = spans[1];
185+
assert.ok(
186+
spanCreateQueryPartitions.spanContext().traceId,
187+
'Expected that createQueryPartitions has a defined traceId'
188+
);
189+
assert.ok(
190+
spanCreatePartitions_.spanContext().traceId,
191+
'Expected that createPartitions_ has a defined traceId'
192+
);
193+
assert.deepStrictEqual(
194+
spanCreatePartitions_.spanContext().traceId,
195+
spanCreateQueryPartitions.spanContext().traceId,
196+
'Expected that both spans share a traceId'
197+
);
198+
assert.ok(
199+
spanCreateQueryPartitions.spanContext().spanId,
200+
'Expected that createQueryPartitions has a defined spanId'
201+
);
202+
assert.ok(
203+
spanCreatePartitions_.spanContext().spanId,
204+
'Expected that createPartitions_ has a defined spanId'
205+
);
206+
assert.deepStrictEqual(
207+
spanCreatePartitions_.parentSpanId,
208+
spanCreateQueryPartitions.spanContext().spanId,
209+
'Expected that createQueryPartitions is the parent to createPartitions_'
210+
);
211+
done();
212+
});
218213
});
219214

220215
it('createReadPartitions', done => {
221216
const REQUEST = sandbox.stub();
222217
const response = {};
223218
REQUEST.callsFake((_, callback) => callback(null, response));
224219

225-
const res = batchTransaction.createReadPartitions(
226-
QUERY,
227-
(err, part, resp) => {
228-
assert.ifError(err);
229-
traceExporter.forceFlush();
230-
const spans = traceExporter.getFinishedSpans();
231-
assert.strictEqual(spans.length, 2, 'Exactly 2 spans expected');
232-
233-
// Sort the spans by duration.
234-
spans.sort((spanA, spanB) => {
235-
spanA.duration < spanB.duration;
236-
});
237-
238-
const actualSpanNames: string[] = [];
239-
spans.forEach(span => {
240-
actualSpanNames.push(span.name);
241-
});
242-
const expectedSpanNames = [
243-
'CloudSpanner.BatchTransaction.createPartitions_',
244-
'CloudSpanner.BatchTransaction.createReadPartitions',
245-
];
246-
assert.deepStrictEqual(
247-
actualSpanNames,
248-
expectedSpanNames,
249-
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
250-
);
251-
done();
252-
}
253-
);
220+
batchTransaction.createReadPartitions(QUERY, err => {
221+
assert.ifError(err);
222+
traceExporter.forceFlush();
223+
const spans = traceExporter.getFinishedSpans();
224+
assert.strictEqual(spans.length, 2, 'Exactly 2 spans expected');
225+
226+
// Sort the spans by duration.
227+
spans.sort((spanA, spanB) => {
228+
spanA.duration < spanB.duration;
229+
});
230+
231+
const actualSpanNames: string[] = [];
232+
spans.forEach(span => {
233+
actualSpanNames.push(span.name);
234+
});
235+
const expectedSpanNames = [
236+
'CloudSpanner.BatchTransaction.createPartitions_',
237+
'CloudSpanner.BatchTransaction.createReadPartitions',
238+
];
239+
assert.deepStrictEqual(
240+
actualSpanNames,
241+
expectedSpanNames,
242+
`span names mismatch:\n\tGot: ${actualSpanNames}\n\tWant: ${expectedSpanNames}`
243+
);
244+
done();
245+
});
254246
});
255247
});

0 commit comments

Comments
 (0)