Skip to content

Commit d8d3bc7

Browse files
committed
feat: add resend attribute to span tracing
1 parent 7fdf9ff commit d8d3bc7

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryAlgorithm.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import static com.google.common.base.Preconditions.checkNotNull;
2020

21+
import com.google.api.core.InternalApi;
2122
import com.google.api.gax.retrying.ResultRetryAlgorithm;
2223
import com.google.api.gax.retrying.ResultRetryAlgorithmWithContext;
2324
import com.google.api.gax.retrying.RetryAlgorithm;
@@ -44,6 +45,17 @@ public class BigQueryRetryAlgorithm<ResponseT> extends RetryAlgorithm<ResponseT>
4445

4546
private static final Logger LOG = Logger.getLogger(BigQueryRetryAlgorithm.class.getName());
4647
private static final UUID RETRY_UUID = UUID.randomUUID();
48+
private static final ThreadLocal<Integer> currentAttempt = ThreadLocal.withInitial(() -> 0);
49+
50+
@InternalApi("internal to java-bigquery")
51+
public static int getCurrentAttempt() {
52+
return currentAttempt.get();
53+
}
54+
55+
@InternalApi("internal to java-bigquery")
56+
public static void setCurrentAttempt(int attempt) {
57+
currentAttempt.set(attempt);
58+
}
4759

4860
public BigQueryRetryAlgorithm(
4961
ResultRetryAlgorithm<ResponseT> resultAlgorithm,
@@ -78,6 +90,9 @@ public boolean shouldRetry(
7890
previousThrowable, bigQueryRetryConfig, previousResponse))
7991
&& shouldRetryBasedOnTiming(context, nextAttemptSettings);
8092

93+
// Store retry attempt count in thread-local storage for tracing
94+
setCurrentAttempt(attemptCount);
95+
8196
if (LOG.isLoggable(Level.FINEST)) {
8297
LOG.log(
8398
Level.FINEST,

java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/telemetry/HttpTracingRequestInitializer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.api.client.http.*;
2020
import com.google.api.core.BetaApi;
2121
import com.google.api.core.InternalApi;
22+
import com.google.cloud.bigquery.BigQueryRetryAlgorithm;
2223
import com.google.common.annotations.VisibleForTesting;
2324
import io.opentelemetry.api.common.AttributeKey;
2425
import io.opentelemetry.api.trace.Span;
@@ -111,6 +112,12 @@ private void addInitialHttpAttributesToSpan(Span span, HttpRequest request) {
111112
span.setAttribute(BigQueryTelemetryTracer.SERVER_PORT, (long) port);
112113
}
113114
span.setAttribute(URL_FULL, getSanitizedUrl(request));
115+
int retryAttempt = BigQueryRetryAlgorithm.getCurrentAttempt();
116+
if (retryAttempt > 0) {
117+
span.setAttribute(HTTP_REQUEST_RESEND_COUNT, (long) retryAttempt);
118+
}
119+
// Reset attempt count to 0 to avoid carrying over state across requests on the same thread
120+
BigQueryRetryAlgorithm.setCurrentAttempt(0);
114121
}
115122

116123
private static void addCommonResponseAttributesToSpan(

java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/spi/v2/HttpBigQueryRpcTest.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,43 @@ public void testHttpTracingEnabled_JsonResponseException_SetsAttributes() throws
11011101
"Invalid request", rpcSpan.getAttributes().get(BigQueryTelemetryTracer.STATUS_MESSAGE));
11021102
assertNull(rpcSpan.getAttributes().get(BigQueryTelemetryTracer.EXCEPTION_TYPE));
11031103
}
1104+
1105+
@Test
1106+
public void testResendCountOnRetry() throws Exception {
1107+
// Manually set attempt count to simulate being inside a retry loop
1108+
com.google.cloud.bigquery.BigQueryRetryAlgorithm.setCurrentAttempt(2);
1109+
1110+
setMockResponse(
1111+
"{\"kind\":\"bigquery#dataset\",\"id\":\""
1112+
+ PROJECT_ID
1113+
+ ":"
1114+
+ DATASET_ID
1115+
+ "\",\"datasetReference\":{\"projectId\":\""
1116+
+ PROJECT_ID
1117+
+ "\",\"datasetId\":\""
1118+
+ DATASET_ID
1119+
+ "\"}}");
1120+
1121+
rpc.getDatasetSkipExceptionTranslation(PROJECT_ID, DATASET_ID, new HashMap<>());
1122+
1123+
List<io.opentelemetry.sdk.trace.data.SpanData> spans = spanExporter.getFinishedSpanItems();
1124+
assertThat(spans).isNotEmpty();
1125+
io.opentelemetry.sdk.trace.data.SpanData rpcSpan =
1126+
spans.stream()
1127+
.filter(s -> s.getName().equals("com.google.cloud.bigquery.BigQueryRpc.getDataset"))
1128+
.findFirst()
1129+
.orElse(null);
1130+
assertNotNull(rpcSpan);
1131+
assertEquals(
1132+
2L,
1133+
rpcSpan
1134+
.getAttributes()
1135+
.get(
1136+
com.google.cloud.bigquery.telemetry.HttpTracingRequestInitializer
1137+
.HTTP_REQUEST_RESEND_COUNT));
1138+
1139+
com.google.cloud.bigquery.BigQueryRetryAlgorithm.setCurrentAttempt(0);
1140+
}
11041141
}
11051142

11061143
@Nested
@@ -1199,6 +1236,42 @@ public void testHttpTracingDisabled_GoogleJsonResponseException_DoesNotSetAttrib
11991236
assertNull(rpcSpan.getAttributes().get(BigQueryTelemetryTracer.ERROR_TYPE));
12001237
assertNull(rpcSpan.getAttributes().get(BigQueryTelemetryTracer.STATUS_MESSAGE));
12011238
}
1239+
1240+
@Test
1241+
public void testResendCountNotSetWhenDisabled() throws Exception {
1242+
// Manually set attempt count to simulate being inside a retry loop
1243+
com.google.cloud.bigquery.BigQueryRetryAlgorithm.setCurrentAttempt(2);
1244+
1245+
setMockResponse(
1246+
"{\"kind\":\"bigquery#dataset\",\"id\":\""
1247+
+ PROJECT_ID
1248+
+ ":"
1249+
+ DATASET_ID
1250+
+ "\",\"datasetReference\":{\"projectId\":\""
1251+
+ PROJECT_ID
1252+
+ "\",\"datasetId\":\""
1253+
+ DATASET_ID
1254+
+ "\"}}");
1255+
1256+
rpc.getDatasetSkipExceptionTranslation(PROJECT_ID, DATASET_ID, new HashMap<>());
1257+
1258+
List<io.opentelemetry.sdk.trace.data.SpanData> spans = spanExporter.getFinishedSpanItems();
1259+
assertThat(spans).isNotEmpty();
1260+
io.opentelemetry.sdk.trace.data.SpanData rpcSpan =
1261+
spans.stream()
1262+
.filter(s -> s.getName().equals("com.google.cloud.bigquery.BigQueryRpc.getDataset"))
1263+
.findFirst()
1264+
.orElse(null);
1265+
assertNotNull(rpcSpan);
1266+
assertNull(
1267+
rpcSpan
1268+
.getAttributes()
1269+
.get(
1270+
com.google.cloud.bigquery.telemetry.HttpTracingRequestInitializer
1271+
.HTTP_REQUEST_RESEND_COUNT));
1272+
1273+
com.google.cloud.bigquery.BigQueryRetryAlgorithm.setCurrentAttempt(0);
1274+
}
12021275
}
12031276

12041277
@Nested

java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/telemetry/HttpTracingRequestInitializerTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,27 @@ public void testAddResponseBodySizeToSpan_NullLength() throws IOException {
335335
assertNull(span.getAttributes().get(HttpTracingRequestInitializer.HTTP_RESPONSE_BODY_SIZE));
336336
}
337337

338+
@Test
339+
public void testResendCountIsSetFromBigQueryRetryAlgorithm() throws IOException {
340+
com.google.cloud.bigquery.BigQueryRetryAlgorithm.setCurrentAttempt(3);
341+
HttpTransport transport = createTransport();
342+
HttpRequest request = buildGetRequest(transport, initializer, BASE_URL);
343+
344+
HttpResponse response = request.execute();
345+
response.disconnect();
346+
347+
spanScope.close();
348+
parentSpan.end();
349+
350+
List<SpanData> spans = spanExporter.getFinishedSpanItems();
351+
assertEquals(1, spans.size());
352+
SpanData span = spans.get(0);
353+
assertEquals(
354+
3L, span.getAttributes().get(HttpTracingRequestInitializer.HTTP_REQUEST_RESEND_COUNT));
355+
356+
com.google.cloud.bigquery.BigQueryRetryAlgorithm.setCurrentAttempt(0);
357+
}
358+
338359
private static HttpTransport createTransport() {
339360
return createTransport(200, null);
340361
}
@@ -429,5 +450,6 @@ private void closeAndVerifySpanData(
429450
} else {
430451
assertNull(span.getAttributes().get(HttpTracingRequestInitializer.HTTP_RESPONSE_BODY_SIZE));
431452
}
453+
assertNull(span.getAttributes().get(HttpTracingRequestInitializer.HTTP_REQUEST_RESEND_COUNT));
432454
}
433455
}

0 commit comments

Comments
 (0)