|
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.OpenTelemetryTraceManager; |
37 | 43 | import com.google.api.gax.tracing.SpanTracer; |
38 | 44 | import com.google.api.gax.tracing.SpanTracerFactory; |
| 45 | +import com.google.rpc.Status; |
39 | 46 | import com.google.showcase.v1beta1.EchoClient; |
40 | 47 | import com.google.showcase.v1beta1.EchoRequest; |
| 48 | +import com.google.showcase.v1beta1.EchoSettings; |
41 | 49 | import com.google.showcase.v1beta1.it.util.TestClientInitializer; |
| 50 | +import com.google.showcase.v1beta1.stub.EchoStub; |
| 51 | +import com.google.showcase.v1beta1.stub.EchoStubSettings; |
42 | 52 | import io.opentelemetry.api.GlobalOpenTelemetry; |
43 | 53 | import io.opentelemetry.api.common.AttributeKey; |
44 | 54 | import io.opentelemetry.api.trace.SpanKind; |
|
48 | 58 | import io.opentelemetry.sdk.trace.data.SpanData; |
49 | 59 | import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; |
50 | 60 | import java.util.List; |
| 61 | +import java.util.Optional; |
51 | 62 | import org.junit.jupiter.api.AfterEach; |
52 | 63 | import org.junit.jupiter.api.BeforeEach; |
53 | 64 | import org.junit.jupiter.api.Test; |
@@ -196,4 +207,136 @@ void testTracing_successfulEcho_httpjson() throws Exception { |
196 | 207 | .isEqualTo("v1beta1/echo:echo"); |
197 | 208 | } |
198 | 209 | } |
| 210 | + |
| 211 | + @Test |
| 212 | + void testTracing_retry_grpc() throws Exception { |
| 213 | + final int attempts = 5; |
| 214 | + final StatusCode.Code statusCode = StatusCode.Code.UNAVAILABLE; |
| 215 | + // A custom EchoClient is used in this test because retries have jitter, and we cannot |
| 216 | + // predict the number of attempts that are scheduled for an RPC invocation otherwise. |
| 217 | + // The custom retrySettings limit to a set number of attempts before the call gives up. |
| 218 | + RetrySettings retrySettings = |
| 219 | + RetrySettings.newBuilder() |
| 220 | + .setTotalTimeout(org.threeten.bp.Duration.ofMillis(5000L)) |
| 221 | + .setMaxAttempts(attempts) |
| 222 | + .build(); |
| 223 | + |
| 224 | + EchoStubSettings.Builder grpcEchoSettingsBuilder = EchoStubSettings.newBuilder(); |
| 225 | + grpcEchoSettingsBuilder |
| 226 | + .echoSettings() |
| 227 | + .setRetrySettings(retrySettings) |
| 228 | + .setRetryableCodes(statusCode); |
| 229 | + EchoSettings grpcEchoSettings = EchoSettings.create(grpcEchoSettingsBuilder.build()); |
| 230 | + grpcEchoSettings = |
| 231 | + grpcEchoSettings.toBuilder() |
| 232 | + .setCredentialsProvider(NoCredentialsProvider.create()) |
| 233 | + .setTransportChannelProvider(EchoSettings.defaultGrpcTransportProviderBuilder().build()) |
| 234 | + .setEndpoint("localhost:7469") |
| 235 | + .build(); |
| 236 | + |
| 237 | + SpanTracerFactory tracingFactory = |
| 238 | + new SpanTracerFactory(new OpenTelemetryTraceManager(openTelemetrySdk)); |
| 239 | + |
| 240 | + EchoStubSettings echoStubSettings = |
| 241 | + (EchoStubSettings) |
| 242 | + grpcEchoSettings.getStubSettings().toBuilder().setTracerFactory(tracingFactory).build(); |
| 243 | + EchoStub stub = echoStubSettings.createStub(); |
| 244 | + EchoClient grpcClient = EchoClient.create(stub); |
| 245 | + |
| 246 | + EchoRequest echoRequest = |
| 247 | + EchoRequest.newBuilder() |
| 248 | + .setError(Status.newBuilder().setCode(statusCode.ordinal()).build()) |
| 249 | + .build(); |
| 250 | + |
| 251 | + assertThrows(UnavailableException.class, () -> grpcClient.echo(echoRequest)); |
| 252 | + |
| 253 | + List<SpanData> spans = spanExporter.getFinishedSpanItems(); |
| 254 | + assertThat(spans).hasSize(attempts); // Expect exactly one span for the successful retry |
| 255 | + |
| 256 | + // This single span represents the successful retry, which has resend_count=1 |
| 257 | + for (int resendCount = 1; resendCount < attempts; resendCount++) { |
| 258 | + Optional<SpanData> found = |
| 259 | + spans.stream() |
| 260 | + .filter( |
| 261 | + span -> |
| 262 | + span.getAttributes() |
| 263 | + .asMap() |
| 264 | + .getOrDefault( |
| 265 | + AttributeKey.longKey( |
| 266 | + ObservabilityAttributes.GRPC_RESEND_COUNT_ATTRIBUTE), |
| 267 | + -1L) |
| 268 | + .equals(1L)) |
| 269 | + .findFirst(); |
| 270 | + assertThat(found).isPresent(); |
| 271 | + } |
| 272 | + } |
| 273 | + |
| 274 | + @Test |
| 275 | + void testTracing_retry_httpjson() throws Exception { |
| 276 | + final int attempts = 5; |
| 277 | + final StatusCode.Code statusCode = StatusCode.Code.UNAVAILABLE; |
| 278 | + // A custom EchoClient is used in this test because retries have jitter, and we cannot |
| 279 | + // predict the number of attempts that are scheduled for an RPC invocation otherwise. |
| 280 | + // The custom retrySettings limit to a set number of attempts before the call gives up. |
| 281 | + RetrySettings retrySettings = |
| 282 | + RetrySettings.newBuilder() |
| 283 | + .setTotalTimeout(org.threeten.bp.Duration.ofMillis(5000L)) |
| 284 | + .setMaxAttempts(attempts) |
| 285 | + .build(); |
| 286 | + |
| 287 | + EchoStubSettings.Builder httpJsonEchoSettingsBuilder = EchoStubSettings.newHttpJsonBuilder(); |
| 288 | + httpJsonEchoSettingsBuilder |
| 289 | + .echoSettings() |
| 290 | + .setRetrySettings(retrySettings) |
| 291 | + .setRetryableCodes(statusCode); |
| 292 | + EchoSettings httpJsonEchoSettings = EchoSettings.create(httpJsonEchoSettingsBuilder.build()); |
| 293 | + httpJsonEchoSettings = |
| 294 | + httpJsonEchoSettings.toBuilder() |
| 295 | + .setCredentialsProvider(NoCredentialsProvider.create()) |
| 296 | + .setTransportChannelProvider( |
| 297 | + EchoSettings.defaultHttpJsonTransportProviderBuilder() |
| 298 | + .setHttpTransport( |
| 299 | + new NetHttpTransport.Builder().doNotValidateCertificate().build()) |
| 300 | + .setEndpoint("http://localhost:7469") |
| 301 | + .build()) |
| 302 | + .build(); |
| 303 | + |
| 304 | + SpanTracerFactory tracingFactory = |
| 305 | + new SpanTracerFactory(new OpenTelemetryTraceManager(openTelemetrySdk)); |
| 306 | + |
| 307 | + EchoStubSettings echoStubSettings = |
| 308 | + (EchoStubSettings) |
| 309 | + httpJsonEchoSettings.getStubSettings().toBuilder() |
| 310 | + .setTracerFactory(tracingFactory) |
| 311 | + .build(); |
| 312 | + EchoStub stub = echoStubSettings.createStub(); |
| 313 | + EchoClient httpClient = EchoClient.create(stub); |
| 314 | + |
| 315 | + EchoRequest echoRequest = |
| 316 | + EchoRequest.newBuilder() |
| 317 | + .setError(Status.newBuilder().setCode(statusCode.ordinal()).build()) |
| 318 | + .build(); |
| 319 | + |
| 320 | + assertThrows(UnavailableException.class, () -> httpClient.echo(echoRequest)); |
| 321 | + |
| 322 | + List<SpanData> spans = spanExporter.getFinishedSpanItems(); |
| 323 | + assertThat(spans).hasSize(attempts); // Expect exactly one span for the successful retry |
| 324 | + |
| 325 | + // This single span represents the successful retry, which has resend_count=1 |
| 326 | + for (int resendCount = 1; resendCount < attempts; resendCount++) { |
| 327 | + Optional<SpanData> found = |
| 328 | + spans.stream() |
| 329 | + .filter( |
| 330 | + span -> |
| 331 | + span.getAttributes() |
| 332 | + .asMap() |
| 333 | + .getOrDefault( |
| 334 | + AttributeKey.longKey( |
| 335 | + ObservabilityAttributes.HTTP_RESEND_COUNT_ATTRIBUTE), |
| 336 | + -1L) |
| 337 | + .equals(1L)) |
| 338 | + .findFirst(); |
| 339 | + assertThat(found).isPresent(); |
| 340 | + } |
| 341 | + } |
199 | 342 | } |
0 commit comments