|
31 | 31 | package com.google.showcase.v1beta1.it; |
32 | 32 |
|
33 | 33 | import static com.google.common.truth.Truth.assertThat; |
| 34 | +import static org.junit.Assert.assertThrows; |
34 | 35 |
|
| 36 | +import com.google.api.client.http.javanet.NetHttpTransport; |
| 37 | +import com.google.api.gax.core.NoCredentialsProvider; |
| 38 | +import com.google.api.gax.retrying.RetrySettings; |
| 39 | +import com.google.api.gax.rpc.StatusCode; |
| 40 | +import com.google.api.gax.rpc.UnavailableException; |
35 | 41 | import com.google.api.gax.tracing.ObservabilityAttributes; |
36 | 42 | import com.google.api.gax.tracing.SpanTracer; |
37 | 43 | import com.google.api.gax.tracing.SpanTracerFactory; |
| 44 | +import com.google.rpc.Status; |
38 | 45 | import com.google.showcase.v1beta1.EchoClient; |
39 | 46 | import com.google.showcase.v1beta1.EchoRequest; |
| 47 | +import com.google.showcase.v1beta1.EchoSettings; |
40 | 48 | import com.google.showcase.v1beta1.it.util.TestClientInitializer; |
| 49 | +import com.google.showcase.v1beta1.stub.EchoStub; |
| 50 | +import com.google.showcase.v1beta1.stub.EchoStubSettings; |
41 | 51 | import io.opentelemetry.api.GlobalOpenTelemetry; |
42 | 52 | import io.opentelemetry.api.common.AttributeKey; |
43 | 53 | import io.opentelemetry.api.trace.SpanKind; |
@@ -205,4 +215,146 @@ void testTracing_successfulEcho_httpjson() throws Exception { |
205 | 215 | .isAtLeast(1L); |
206 | 216 | } |
207 | 217 | } |
| 218 | + |
| 219 | + @Test |
| 220 | + void testTracing_retry_grpc() throws Exception { |
| 221 | + final int attempts = 5; |
| 222 | + final StatusCode.Code statusCode = StatusCode.Code.UNAVAILABLE; |
| 223 | + // A custom EchoClient is used in this test because retries have jitter, and we cannot |
| 224 | + // predict the number of attempts that are scheduled for an RPC invocation otherwise. |
| 225 | + // The custom retrySettings limit to a set number of attempts before the call gives up. |
| 226 | + RetrySettings retrySettings = |
| 227 | + RetrySettings.newBuilder() |
| 228 | + .setTotalTimeout(org.threeten.bp.Duration.ofMillis(5000L)) |
| 229 | + .setMaxAttempts(attempts) |
| 230 | + .build(); |
| 231 | + |
| 232 | + EchoStubSettings.Builder grpcEchoSettingsBuilder = EchoStubSettings.newBuilder(); |
| 233 | + grpcEchoSettingsBuilder |
| 234 | + .echoSettings() |
| 235 | + .setRetrySettings(retrySettings) |
| 236 | + .setRetryableCodes(statusCode); |
| 237 | + EchoSettings grpcEchoSettings = EchoSettings.create(grpcEchoSettingsBuilder.build()); |
| 238 | + grpcEchoSettings = |
| 239 | + grpcEchoSettings.toBuilder() |
| 240 | + .setCredentialsProvider(NoCredentialsProvider.create()) |
| 241 | + .setTransportChannelProvider(EchoSettings.defaultGrpcTransportProviderBuilder().build()) |
| 242 | + .setEndpoint("localhost:7469") |
| 243 | + .build(); |
| 244 | + |
| 245 | + SpanTracerFactory tracingFactory = new SpanTracerFactory(openTelemetrySdk); |
| 246 | + |
| 247 | + EchoStubSettings echoStubSettings = |
| 248 | + (EchoStubSettings) |
| 249 | + grpcEchoSettings.getStubSettings().toBuilder().setTracerFactory(tracingFactory).build(); |
| 250 | + EchoStub stub = echoStubSettings.createStub(); |
| 251 | + EchoClient grpcClient = EchoClient.create(stub); |
| 252 | + |
| 253 | + EchoRequest echoRequest = |
| 254 | + EchoRequest.newBuilder() |
| 255 | + .setError(Status.newBuilder().setCode(statusCode.ordinal()).build()) |
| 256 | + .build(); |
| 257 | + |
| 258 | + assertThrows(UnavailableException.class, () -> grpcClient.echo(echoRequest)); |
| 259 | + |
| 260 | + List<SpanData> spans = spanExporter.getFinishedSpanItems(); |
| 261 | + assertThat(spans).hasSize(attempts); // Expect exactly one span for the successful retry |
| 262 | + |
| 263 | + // This single span represents the successful retry, which has resend_count=1 |
| 264 | + // The first attempt has no resend_count. The subsequent retries will have a resend_count, |
| 265 | + // starting from 1. |
| 266 | + List<Long> resendCounts = |
| 267 | + spans.stream() |
| 268 | + .map( |
| 269 | + span -> |
| 270 | + (Long) |
| 271 | + span.getAttributes() |
| 272 | + .asMap() |
| 273 | + .get( |
| 274 | + AttributeKey.longKey( |
| 275 | + ObservabilityAttributes.GRPC_RESEND_COUNT_ATTRIBUTE))) |
| 276 | + .filter(java.util.Objects::nonNull) |
| 277 | + .sorted() |
| 278 | + .collect(java.util.stream.Collectors.toList()); |
| 279 | + |
| 280 | + List<Long> expectedCounts = |
| 281 | + java.util.stream.LongStream.range(1, attempts) |
| 282 | + .boxed() |
| 283 | + .collect(java.util.stream.Collectors.toList()); |
| 284 | + assertThat(resendCounts).containsExactlyElementsIn(expectedCounts).inOrder(); |
| 285 | + } |
| 286 | + |
| 287 | + @Test |
| 288 | + void testTracing_retry_httpjson() throws Exception { |
| 289 | + final int attempts = 5; |
| 290 | + final StatusCode.Code statusCode = StatusCode.Code.UNAVAILABLE; |
| 291 | + // A custom EchoClient is used in this test because retries have jitter, and we cannot |
| 292 | + // predict the number of attempts that are scheduled for an RPC invocation otherwise. |
| 293 | + // The custom retrySettings limit to a set number of attempts before the call gives up. |
| 294 | + RetrySettings retrySettings = |
| 295 | + RetrySettings.newBuilder() |
| 296 | + .setTotalTimeout(org.threeten.bp.Duration.ofMillis(5000L)) |
| 297 | + .setMaxAttempts(attempts) |
| 298 | + .build(); |
| 299 | + |
| 300 | + EchoStubSettings.Builder httpJsonEchoSettingsBuilder = EchoStubSettings.newHttpJsonBuilder(); |
| 301 | + httpJsonEchoSettingsBuilder |
| 302 | + .echoSettings() |
| 303 | + .setRetrySettings(retrySettings) |
| 304 | + .setRetryableCodes(statusCode); |
| 305 | + EchoSettings httpJsonEchoSettings = EchoSettings.create(httpJsonEchoSettingsBuilder.build()); |
| 306 | + httpJsonEchoSettings = |
| 307 | + httpJsonEchoSettings.toBuilder() |
| 308 | + .setCredentialsProvider(NoCredentialsProvider.create()) |
| 309 | + .setTransportChannelProvider( |
| 310 | + EchoSettings.defaultHttpJsonTransportProviderBuilder() |
| 311 | + .setHttpTransport( |
| 312 | + new NetHttpTransport.Builder().doNotValidateCertificate().build()) |
| 313 | + .setEndpoint("http://localhost:7469") |
| 314 | + .build()) |
| 315 | + .build(); |
| 316 | + |
| 317 | + SpanTracerFactory tracingFactory = new SpanTracerFactory(openTelemetrySdk); |
| 318 | + |
| 319 | + EchoStubSettings echoStubSettings = |
| 320 | + (EchoStubSettings) |
| 321 | + httpJsonEchoSettings.getStubSettings().toBuilder() |
| 322 | + .setTracerFactory(tracingFactory) |
| 323 | + .build(); |
| 324 | + EchoStub stub = echoStubSettings.createStub(); |
| 325 | + EchoClient httpClient = EchoClient.create(stub); |
| 326 | + |
| 327 | + EchoRequest echoRequest = |
| 328 | + EchoRequest.newBuilder() |
| 329 | + .setError(Status.newBuilder().setCode(statusCode.ordinal()).build()) |
| 330 | + .build(); |
| 331 | + |
| 332 | + assertThrows(UnavailableException.class, () -> httpClient.echo(echoRequest)); |
| 333 | + |
| 334 | + List<SpanData> spans = spanExporter.getFinishedSpanItems(); |
| 335 | + assertThat(spans).hasSize(attempts); // Expect exactly one span for the successful retry |
| 336 | + |
| 337 | + // This single span represents the successful retry, which has resend_count=1 |
| 338 | + // The first attempt has no resend_count. The subsequent retries will have a resend_count, |
| 339 | + // starting from 1. |
| 340 | + List<Long> resendCounts = |
| 341 | + spans.stream() |
| 342 | + .map( |
| 343 | + span -> |
| 344 | + (Long) |
| 345 | + span.getAttributes() |
| 346 | + .asMap() |
| 347 | + .get( |
| 348 | + AttributeKey.longKey( |
| 349 | + ObservabilityAttributes.HTTP_RESEND_COUNT_ATTRIBUTE))) |
| 350 | + .filter(java.util.Objects::nonNull) |
| 351 | + .sorted() |
| 352 | + .collect(java.util.stream.Collectors.toList()); |
| 353 | + |
| 354 | + List<Long> expectedCounts = |
| 355 | + java.util.stream.LongStream.range(1, attempts) |
| 356 | + .boxed() |
| 357 | + .collect(java.util.stream.Collectors.toList()); |
| 358 | + assertThat(resendCounts).containsExactlyElementsIn(expectedCounts).inOrder(); |
| 359 | + } |
208 | 360 | } |
0 commit comments