Skip to content

Commit af18f65

Browse files
keshavdandevalogachevcloud-java-bot
authored
feat(bigquery-jdbc): OpenTelemetry integration in BQ JDBC (#12902)
b/342411096 --------- Co-authored-by: Kirill Logachev <kirl@google.com> Co-authored-by: cloud-java-bot <cloud-java-bot@google.com>
1 parent 41a7a52 commit af18f65

28 files changed

Lines changed: 2805 additions & 283 deletions

java-bigquery-jdbc/pom.xml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@
8484
<ignoredUsedUndeclaredDependency>org.apache.httpcomponents.*:*</ignoredUsedUndeclaredDependency>
8585
<ignoredUsedUndeclaredDependency>io.grpc:*</ignoredUsedUndeclaredDependency>
8686
</ignoredUsedUndeclaredDependencies>
87+
<ignoredUnusedDeclaredDependencies combine.children="append">
88+
<ignoredUnusedDeclaredDependency>io.opentelemetry:opentelemetry-exporter-otlp</ignoredUnusedDeclaredDependency>
89+
</ignoredUnusedDeclaredDependencies>
90+
<ignoredDependencies>
91+
<ignoredDependency>io.opentelemetry:opentelemetry-sdk-logs</ignoredDependency>
92+
<ignoredDependency>io.opentelemetry:opentelemetry-sdk-common</ignoredDependency>
93+
</ignoredDependencies>
8794
</configuration>
8895
</plugin>
8996
<plugin>
@@ -173,6 +180,17 @@
173180
<relocation>
174181
<pattern>io.opentelemetry</pattern>
175182
<shadedPattern>com.google.bqjdbc.shaded.io.opentelemetry</shadedPattern>
183+
<excludes>
184+
<!--
185+
OpenTelemetry API and Context must remain unshaded to ensure interoperability.
186+
Shading these would prevent the driver from participating in the application's
187+
existing tracing context. We are aware that unshaded dependencies can lead to
188+
version mismatches, but this is a necessary trade-off for the OpenTelemetry
189+
integration to function correctly across different applications.
190+
-->
191+
<exclude>io.opentelemetry.api.**</exclude>
192+
<exclude>io.opentelemetry.context.**</exclude>
193+
</excludes>
176194
</relocation>
177195
<relocation>
178196
<pattern>io.perfmark</pattern>
@@ -223,6 +241,11 @@
223241
<artifactId>google-cloud-bigquerystorage</artifactId>
224242
<version>3.31.0-SNAPSHOT</version><!-- {x-version-update:google-cloud-bigquerystorage:current} -->
225243
</dependency>
244+
<dependency>
245+
<groupId>com.google.cloud</groupId>
246+
<artifactId>google-cloud-logging</artifactId>
247+
<version>3.32.0</version><!-- {x-version-update:google-cloud-logging:current} -->
248+
</dependency>
226249
<dependency>
227250
<groupId>com.google.http-client</groupId>
228251
<artifactId>google-http-client-apache-v5</artifactId>
@@ -310,6 +333,16 @@
310333
<groupId>io.grpc</groupId>
311334
<artifactId>grpc-netty-shaded</artifactId>
312335
</dependency>
336+
<dependency>
337+
<groupId>io.grpc</groupId>
338+
<artifactId>grpc-opentelemetry</artifactId>
339+
<exclusions>
340+
<exclusion>
341+
<groupId>io.opentelemetry</groupId>
342+
<artifactId>opentelemetry-api</artifactId>
343+
</exclusion>
344+
</exclusions>
345+
</dependency>
313346

314347
<dependency>
315348
<groupId>org.apache.arrow</groupId>
@@ -328,6 +361,41 @@
328361
<artifactId>httpcore5</artifactId>
329362
</dependency>
330363

364+
<!-- OpenTelemetry APIs (unshaded) -->
365+
<dependency>
366+
<groupId>io.opentelemetry</groupId>
367+
<artifactId>opentelemetry-api</artifactId>
368+
</dependency>
369+
<dependency>
370+
<groupId>io.opentelemetry</groupId>
371+
<artifactId>opentelemetry-context</artifactId>
372+
</dependency>
373+
<!-- OpenTelemetry SDK and Exporters (will be shaded via existing 'io' relocation) -->
374+
<dependency>
375+
<groupId>io.opentelemetry</groupId>
376+
<artifactId>opentelemetry-sdk</artifactId>
377+
</dependency>
378+
<dependency>
379+
<groupId>io.opentelemetry</groupId>
380+
<artifactId>opentelemetry-sdk-trace</artifactId>
381+
</dependency>
382+
<dependency>
383+
<groupId>io.opentelemetry</groupId>
384+
<artifactId>opentelemetry-exporter-otlp</artifactId>
385+
</dependency>
386+
<dependency>
387+
<groupId>io.opentelemetry</groupId>
388+
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
389+
</dependency>
390+
<dependency>
391+
<groupId>io.opentelemetry</groupId>
392+
<artifactId>opentelemetry-sdk-extension-autoconfigure-spi</artifactId>
393+
</dependency>
394+
<dependency>
395+
<groupId>io.opentelemetry</groupId>
396+
<artifactId>opentelemetry-sdk-logs</artifactId>
397+
</dependency>
398+
331399
<!-- Test Dependencies -->
332400

333401
<dependency>
@@ -377,6 +445,17 @@
377445
<artifactId>junit-platform-suite-engine</artifactId>
378446
<scope>test</scope>
379447
</dependency>
448+
<dependency>
449+
<groupId>io.opentelemetry</groupId>
450+
<artifactId>opentelemetry-sdk-testing</artifactId>
451+
<scope>test</scope>
452+
</dependency>
453+
<dependency>
454+
<groupId>com.google.cloud</groupId>
455+
<artifactId>google-cloud-trace</artifactId>
456+
<version>2.92.0</version><!-- {x-version-update:google-cloud-trace:current} -->
457+
<scope>test</scope>
458+
</dependency>
380459
</dependencies>
381460

382461
<profiles>

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.google.cloud.bigquery.exception.BigQueryJdbcRuntimeException;
2929
import com.google.cloud.bigquery.storage.v1.ArrowRecordBatch;
3030
import com.google.cloud.bigquery.storage.v1.ArrowSchema;
31+
import io.opentelemetry.context.Scope;
3132
import java.io.IOException;
3233
import java.math.BigDecimal;
3334
import java.sql.Date;
@@ -264,29 +265,31 @@ public boolean next() throws SQLException {
264265
|| this.currentBatchRowIndex == (this.vectorSchemaRoot.getRowCount() - 1)) {
265266
/* Start of iteration or we have exhausted the current batch */
266267
// Advance the cursor. Potentially blocking operation.
267-
BigQueryArrowBatchWrapper batchWrapper = this.buffer.take();
268-
if (batchWrapper.getException() != null) {
269-
throw new BigQueryJdbcRuntimeException(batchWrapper.getException());
270-
}
271-
if (batchWrapper.isLast()) {
272-
/* Marks the end of the records */
273-
if (this.vectorSchemaRoot != null) {
274-
// IMP: To avoid memory leak: clear vectorSchemaRoot as it still holds
275-
// the last batch
276-
this.vectorSchemaRoot.clear();
268+
try (Scope scope = makeOriginalContextCurrent()) {
269+
BigQueryArrowBatchWrapper batchWrapper = this.buffer.take();
270+
if (batchWrapper.getException() != null) {
271+
throw new BigQueryJdbcRuntimeException(batchWrapper.getException());
272+
}
273+
if (batchWrapper.isLast()) {
274+
/* Marks the end of the records */
275+
if (this.vectorSchemaRoot != null) {
276+
// IMP: To avoid memory leak: clear vectorSchemaRoot as it still holds
277+
// the last batch
278+
this.vectorSchemaRoot.clear();
279+
}
280+
this.hasReachedEnd = true;
281+
this.rowCount++;
282+
return false;
277283
}
278-
this.hasReachedEnd = true;
284+
// Valid batch, process it
285+
ArrowRecordBatch arrowBatch = batchWrapper.getCurrentArrowBatch();
286+
// Populates vectorSchemaRoot
287+
this.arrowDeserializer.deserializeArrowBatch(arrowBatch);
288+
// Pointing to the first row in this fresh batch
289+
this.currentBatchRowIndex = 0;
279290
this.rowCount++;
280-
return false;
291+
return true;
281292
}
282-
// Valid batch, process it
283-
ArrowRecordBatch arrowBatch = batchWrapper.getCurrentArrowBatch();
284-
// Populates vectorSchemaRoot
285-
this.arrowDeserializer.deserializeArrowBatch(arrowBatch);
286-
// Pointing to the first row in this fresh batch
287-
this.currentBatchRowIndex = 0;
288-
this.rowCount++;
289-
return true;
290293
}
291294
// There are rows left in the current batch.
292295
else if (this.currentBatchRowIndex < this.vectorSchemaRoot.getRowCount()) {

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
import com.google.cloud.bigquery.exception.BigQueryJdbcCoercionException;
3131
import com.google.cloud.bigquery.exception.BigQueryJdbcCoercionNotFoundException;
3232
import com.google.cloud.bigquery.exception.BigQueryJdbcException;
33+
import io.opentelemetry.api.trace.Span;
34+
import io.opentelemetry.api.trace.SpanContext;
35+
import io.opentelemetry.context.Context;
36+
import io.opentelemetry.context.Scope;
3337
import java.io.InputStream;
3438
import java.io.Reader;
3539
import java.io.StringReader;
@@ -67,6 +71,7 @@ public abstract class BigQueryBaseResultSet extends BigQueryNoOpsResultSet
6771
private SQLWarning warnings;
6872
private boolean warningsLoaded = false;
6973
protected final BigQueryTypeCoercer bigQueryTypeCoercer = BigQueryTypeCoercionUtility.INSTANCE;
74+
protected final SpanContext originalSpanContext;
7075

7176
protected BigQueryBaseResultSet(
7277
BigQuery bigQuery, BigQueryStatement statement, Schema schema, boolean isNested) {
@@ -80,6 +85,7 @@ protected BigQueryBaseResultSet(
8085
this.schema = schema;
8186
this.schemaFieldList = schema != null ? schema.getFields() : null;
8287
this.isNested = isNested;
88+
this.originalSpanContext = Span.current().getSpanContext();
8389
this.job = job;
8490
if (job != null) {
8591
this.jobId = job.getJobId();
@@ -89,6 +95,10 @@ protected BigQueryBaseResultSet(
8995
this.getClass(), statement != null ? statement.connectionId : null);
9096
}
9197

98+
protected Scope makeOriginalContextCurrent() {
99+
return Context.current().with(Span.wrap(this.originalSpanContext)).makeCurrent();
100+
}
101+
92102
public QueryStatistics getQueryStatistics() {
93103
if (queryStatistics != null) {
94104
return queryStatistics;

0 commit comments

Comments
 (0)