3131import static com .google .cloud .datastore .telemetry .TraceUtil .SPAN_NAME_TRANSACTION_RUN_QUERY ;
3232import static com .google .common .truth .Truth .assertThat ;
3333import static io .opentelemetry .semconv .resource .attributes .ResourceAttributes .SERVICE_NAME ;
34+ import static org .awaitility .Awaitility .await ;
3435import static org .junit .Assert .assertEquals ;
3536import static org .junit .Assert .assertFalse ;
3637import static org .junit .Assert .assertNotNull ;
3738import static org .junit .Assert .assertNull ;
3839import static org .junit .Assert .assertTrue ;
3940
4041import com .google .api .gax .core .FixedCredentialsProvider ;
41- import com .google .api .gax .rpc .DeadlineExceededException ;
42- import com .google .api .gax .rpc .NotFoundException ;
4342import com .google .auth .Credentials ;
4443import com .google .cloud .datastore .AggregationQuery ;
4544import com .google .cloud .datastore .AggregationResult ;
8180import io .opentelemetry .sdk .trace .export .BatchSpanProcessor ;
8281import io .opentelemetry .sdk .trace .samplers .Sampler ;
8382import java .io .IOException ;
83+ import java .time .Duration ;
8484import java .util .ArrayList ;
8585import java .util .Arrays ;
8686import java .util .Collections ;
8989import java .util .Random ;
9090import java .util .TreeMap ;
9191import java .util .concurrent .TimeUnit ;
92+ import java .util .concurrent .atomic .AtomicReference ;
9293import java .util .logging .Level ;
9394import java .util .logging .Logger ;
95+ import org .awaitility .core .ConditionTimeoutException ;
9496import org .junit .After ;
9597import org .junit .AfterClass ;
9698import org .junit .Before ;
@@ -474,44 +476,35 @@ protected void waitForTracesToComplete() throws Exception {
474476 protected void fetchAndValidateTrace (
475477 String traceId , int numExpectedSpans , List <List <String >> callStackList )
476478 throws InterruptedException {
477- // Large enough count to accommodate eventually consistent Cloud Trace backend
478- int numRetries = GET_TRACE_RETRY_COUNT ;
479479 // Account for rootSpanName
480- numExpectedSpans ++;
481-
482- // Fetch traces
483- do {
484- try {
485- retrievedTrace = traceClient .getTrace (projectId , traceId );
486- assertEquals (traceId , retrievedTrace .getTraceId ());
487-
488- logger .info (
489- "expectedSpanCount="
490- + numExpectedSpans
491- + ", retrievedSpanCount="
492- + retrievedTrace .getSpansCount ());
493- } catch (NotFoundException | DeadlineExceededException e ) {
494- logger .info (
495- "Trace not found or deadline exceeded, retrying in "
496- + GET_TRACE_RETRY_BACKOFF_MILLIS
497- + " ms" );
498- } catch (IndexOutOfBoundsException outOfBoundsException ) {
499- logger .info ("Call stack not found in trace. Retrying." );
500- }
501- if (retrievedTrace == null || numExpectedSpans != retrievedTrace .getSpansCount ()) {
502- Thread .sleep (GET_TRACE_RETRY_BACKOFF_MILLIS );
503- }
504- } while (numRetries -- > 0
505- && (retrievedTrace == null || numExpectedSpans != retrievedTrace .getSpansCount ()));
480+ final int expectedSpanCount = numExpectedSpans + 1 ;
506481
507- if (retrievedTrace == null || numExpectedSpans != retrievedTrace .getSpansCount ()) {
482+ try {
483+ await ()
484+ .atMost (Duration .ofMillis ((long ) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS ))
485+ .pollInterval (Duration .ofMillis (GET_TRACE_RETRY_BACKOFF_MILLIS ))
486+ .ignoreExceptionsInstanceOf (Throwable .class )
487+ .until (
488+ () -> {
489+ retrievedTrace = traceClient .getTrace (projectId , traceId );
490+ assertEquals (traceId , retrievedTrace .getTraceId ());
491+ logger .info (
492+ "expectedSpanCount="
493+ + expectedSpanCount
494+ + ", retrievedSpanCount="
495+ + retrievedTrace .getSpansCount ());
496+ return retrievedTrace != null
497+ && expectedSpanCount == retrievedTrace .getSpansCount ();
498+ });
499+ } catch (ConditionTimeoutException e ) {
508500 throw new RuntimeException (
509501 "Expected number of spans: "
510- + numExpectedSpans
502+ + expectedSpanCount
511503 + ", Actual number of spans: "
512504 + (retrievedTrace != null
513505 ? retrievedTrace .getSpansList ().toString ()
514- : "Trace NOT_FOUND" ));
506+ : "Trace NOT_FOUND" ),
507+ e );
515508 }
516509
517510 TraceContainer traceContainer = new TraceContainer (rootSpanName , retrievedTrace );
@@ -527,6 +520,7 @@ protected void fetchAndValidateTrace(
527520 logger .info ("Checking if TraceContainer contains the callStack" );
528521 String [] expectedCallList = new String [expectedCallStack .size ()];
529522 if (!traceContainer .containsCallStack (expectedCallStack .toArray (expectedCallList ))) {
523+ logger .severe ("CallStack not found in TraceContainer." );
530524 throw new RuntimeException (
531525 "Expected spans: "
532526 + Arrays .toString (expectedCallList )
@@ -535,7 +529,6 @@ protected void fetchAndValidateTrace(
535529 ? retrievedTrace .getSpansList ().toString ()
536530 : "Trace NOT_FOUND" ));
537531 }
538- logger .severe ("CallStack not found in TraceContainer." );
539532 }
540533 }
541534
@@ -563,27 +556,32 @@ public void traceContainerTest() throws Exception {
563556 }
564557 waitForTracesToComplete ();
565558
566- Trace traceResp = null ;
559+ AtomicReference < Trace > traceRespHolder = new AtomicReference <>() ;
567560 int expectedSpanCount = 2 ;
568561
569- int numRetries = GET_TRACE_RETRY_COUNT ;
570- do {
571- try {
572- traceResp = traceClient .getTrace (projectId , customSpanContext .getTraceId ());
573- if (traceResp .getSpansCount () == expectedSpanCount ) {
574- logger .info ("Success: Got " + expectedSpanCount + " spans." );
575- break ;
576- }
577- } catch (NotFoundException notFoundException ) {
578- Thread .sleep (GET_TRACE_RETRY_BACKOFF_MILLIS );
579- logger .info ("Trace not found, retrying in " + GET_TRACE_RETRY_BACKOFF_MILLIS + " ms" );
580- }
581- logger .info (
582- "Trace Found. The trace did not contain "
583- + expectedSpanCount
584- + " spans. Going to retry." );
585- numRetries --;
586- } while (numRetries > 0 );
562+ try {
563+ await ()
564+ .atMost (Duration .ofMillis ((long ) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS ))
565+ .pollInterval (Duration .ofMillis (GET_TRACE_RETRY_BACKOFF_MILLIS ))
566+ .ignoreExceptionsInstanceOf (Throwable .class )
567+ .until (
568+ () -> {
569+ Trace trace = traceClient .getTrace (projectId , customSpanContext .getTraceId ());
570+ traceRespHolder .set (trace );
571+ if (trace .getSpansCount () == expectedSpanCount ) {
572+ logger .info ("Success: Got " + expectedSpanCount + " spans." );
573+ return true ;
574+ }
575+ logger .info (
576+ "Trace Found. The trace did not contain "
577+ + expectedSpanCount
578+ + " spans. Going to retry." );
579+ return false ;
580+ });
581+ } catch (ConditionTimeoutException ignored ) {
582+ // Ignore to let assertions below run and fail with descriptive messages
583+ }
584+ Trace traceResp = traceRespHolder .get ();
587585
588586 // Make sure we got as many spans as we expected.
589587 assertNotNull (traceResp );
0 commit comments