From 56db71a814fbb59a00f9b6ce781eb82f74af69d6 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 6 Jul 2026 19:11:27 +0000 Subject: [PATCH 01/13] test(datastore): migrate sleep-based polling to Awaitility Migrate sleep-based polling loops in AbstractITDatastoreTest and ITE2ETracingTest to use Awaitility for more robust and faster test execution. - Replace while-loop polling for strong consistency in AbstractITDatastoreTest.getStronglyConsistentResults with Awaitility await(). - Replace do-while loop polling for traces in ITE2ETracingTest.fetchAndValidateTrace and traceContainerTest with Awaitility await(). - Add Awaitility dependency to google-cloud-datastore pom.xml. - Add comments explaining why Thread.sleep is kept in other places (readTime tests and JUnit retry rule) as requested. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- java-datastore/google-cloud-datastore/pom.xml | 6 ++ .../datastore/it/AbstractITDatastoreTest.java | 51 ++++++--- .../cloud/datastore/it/ITE2ETracingTest.java | 102 +++++++++--------- .../datastore/it/MultipleAttemptsRule.java | 3 + 4 files changed, 97 insertions(+), 65 deletions(-) diff --git a/java-datastore/google-cloud-datastore/pom.xml b/java-datastore/google-cloud-datastore/pom.xml index 64715b4b8301..c9fb4829558c 100644 --- a/java-datastore/google-cloud-datastore/pom.xml +++ b/java-datastore/google-cloud-datastore/pom.xml @@ -245,6 +245,12 @@ 0.15.0 test + + org.awaitility + awaitility + 4.3.0 + test + com.google.cloud google-cloud-trace diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java index 53a858cae91a..d5d9c437a4de 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java @@ -34,7 +34,10 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.awaitility.Awaitility.await; +import java.time.Duration; +import org.awaitility.core.ConditionTimeoutException; import com.google.cloud.Timestamp; import com.google.cloud.Tuple; import com.google.cloud.datastore.AggregationQuery; @@ -282,21 +285,29 @@ private Iterator getStronglyConsistentResults(Query scQuery, Query query) QueryResults scResults = datastore.run(scQuery); List scResultsCopy = makeResultsCopy(scResults); Set scResultsSet = new HashSet<>(scResultsCopy); - int maxAttempts = 20; - - while (maxAttempts > 0) { - --maxAttempts; - QueryResults results = datastore.run(query); - List resultsCopy = makeResultsCopy(results); - Set resultsSet = new HashSet<>(resultsCopy); - if (scResultsSet.size() == resultsSet.size() && scResultsSet.containsAll(resultsSet)) { - return resultsCopy.iterator(); - } - Thread.sleep(500); + @SuppressWarnings("unchecked") + final List[] finalResults = new List[1]; + try { + await() + .atMost(Duration.ofSeconds(10)) + .pollInterval(Duration.ofMillis(500)) + .until( + () -> { + QueryResults results = datastore.run(query); + List resultsCopy = makeResultsCopy(results); + Set resultsSet = new HashSet<>(resultsCopy); + if (scResultsSet.size() == resultsSet.size() + && scResultsSet.containsAll(resultsSet)) { + finalResults[0] = resultsCopy; + return true; + } + return false; + }); + } catch (ConditionTimeoutException e) { + throw new RuntimeException( + "reached max number of attempts to get strongly consistent results.", e); } - - throw new RuntimeException( - "reached max number of attempts to get strongly consistent results."); + return finalResults[0].iterator(); } private List makeResultsCopy(QueryResults scResults) { @@ -1331,8 +1342,12 @@ private void testCountAggregationReadTimeWith(Consumer .build(); datastore.put(entity1, entity2); + // Sleep to ensure time passes so that the subsequent Timestamp.now() is strictly + // after the commit time of entity1 and entity2. Thread.sleep(1000); Timestamp now = Timestamp.now(); + // Sleep to ensure time passes so that the subsequent write of entity3 is strictly + // after 'now'. This allows testing ReadOption.readTime(now) deterministically. Thread.sleep(1000); datastore.put(entity3); @@ -1784,8 +1799,12 @@ public void testGetWithReadTime() throws InterruptedException { try { datastore.put(Entity.newBuilder(key).set("str", "old_str_value").build()); + // Sleep to ensure time passes so that the subsequent Timestamp.now() is strictly + // after the commit time of the old entity value. Thread.sleep(1000); Timestamp now = Timestamp.now(); + // Sleep to ensure time passes so that the subsequent write of the new entity value + // is strictly after 'now'. This allows testing ReadOption.readTime(now) deterministically. Thread.sleep(1000); datastore.put(Entity.newBuilder(key).set("str", "new_str_value").build()); @@ -2102,8 +2121,12 @@ public void testQueryWithReadTime() throws InterruptedException { .build(); datastore.put(entity1, entity2); + // Sleep to ensure time passes so that the subsequent Timestamp.now() is strictly + // after the commit time of entity1 and entity2. Thread.sleep(1000); Timestamp now = Timestamp.now(); + // Sleep to ensure time passes so that the subsequent write of entity3 is strictly + // after 'now'. This allows testing ReadOption.readTime(now) deterministically. Thread.sleep(1000); datastore.put(entity3); diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index 63a80fe1913f..dfe39c37a603 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -36,7 +36,10 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.awaitility.Awaitility.await; +import java.time.Duration; +import org.awaitility.core.ConditionTimeoutException; import com.google.api.gax.core.FixedCredentialsProvider; import com.google.api.gax.rpc.DeadlineExceededException; import com.google.api.gax.rpc.NotFoundException; @@ -452,44 +455,36 @@ protected void waitForTracesToComplete() throws Exception { protected void fetchAndValidateTrace( String traceId, int numExpectedSpans, List> callStackList) throws InterruptedException { - // Large enough count to accommodate eventually consistent Cloud Trace backend - int numRetries = GET_TRACE_RETRY_COUNT; // Account for rootSpanName - numExpectedSpans++; - - // Fetch traces - do { - try { - retrievedTrace = traceClient.getTrace(projectId, traceId); - assertEquals(traceId, retrievedTrace.getTraceId()); - - logger.info( - "expectedSpanCount=" - + numExpectedSpans - + ", retrievedSpanCount=" - + retrievedTrace.getSpansCount()); - } catch (NotFoundException | DeadlineExceededException e) { - logger.info( - "Trace not found or deadline exceeded, retrying in " - + GET_TRACE_RETRY_BACKOFF_MILLIS - + " ms"); - } catch (IndexOutOfBoundsException outOfBoundsException) { - logger.info("Call stack not found in trace. Retrying."); - } - if (retrievedTrace == null || numExpectedSpans != retrievedTrace.getSpansCount()) { - Thread.sleep(GET_TRACE_RETRY_BACKOFF_MILLIS); - } - } while (numRetries-- > 0 - && (retrievedTrace == null || numExpectedSpans != retrievedTrace.getSpansCount())); - - if (retrievedTrace == null || numExpectedSpans != retrievedTrace.getSpansCount()) { + final int expectedSpanCount = numExpectedSpans + 1; + + try { + await() + .atMost(Duration.ofSeconds(GET_TRACE_RETRY_COUNT)) + .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) + .ignoreExceptionsInstanceOf(NotFoundException.class) + .ignoreExceptionsInstanceOf(DeadlineExceededException.class) + .ignoreExceptionsInstanceOf(IndexOutOfBoundsException.class) + .until( + () -> { + retrievedTrace = traceClient.getTrace(projectId, traceId); + assertEquals(traceId, retrievedTrace.getTraceId()); + logger.info( + "expectedSpanCount=" + + expectedSpanCount + + ", retrievedSpanCount=" + + retrievedTrace.getSpansCount()); + return retrievedTrace != null && expectedSpanCount == retrievedTrace.getSpansCount(); + }); + } catch (ConditionTimeoutException e) { throw new RuntimeException( "Expected number of spans: " - + numExpectedSpans + + expectedSpanCount + ", Actual number of spans: " + (retrievedTrace != null ? retrievedTrace.getSpansList().toString() - : "Trace NOT_FOUND")); + : "Trace NOT_FOUND"), + e); } TraceContainer traceContainer = new TraceContainer(rootSpanName, retrievedTrace); @@ -541,27 +536,32 @@ public void traceContainerTest() throws Exception { } waitForTracesToComplete(); - Trace traceResp = null; + final Trace[] traceRespHolder = new Trace[1]; int expectedSpanCount = 2; - int numRetries = GET_TRACE_RETRY_COUNT; - do { - try { - traceResp = traceClient.getTrace(projectId, customSpanContext.getTraceId()); - if (traceResp.getSpansCount() == expectedSpanCount) { - logger.info("Success: Got " + expectedSpanCount + " spans."); - break; - } - } catch (NotFoundException notFoundException) { - Thread.sleep(GET_TRACE_RETRY_BACKOFF_MILLIS); - logger.info("Trace not found, retrying in " + GET_TRACE_RETRY_BACKOFF_MILLIS + " ms"); - } - logger.info( - "Trace Found. The trace did not contain " - + expectedSpanCount - + " spans. Going to retry."); - numRetries--; - } while (numRetries > 0); + try { + await() + .atMost(Duration.ofSeconds(GET_TRACE_RETRY_COUNT)) + .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) + .ignoreExceptionsInstanceOf(NotFoundException.class) + .until( + () -> { + Trace trace = traceClient.getTrace(projectId, customSpanContext.getTraceId()); + traceRespHolder[0] = trace; + if (trace.getSpansCount() == expectedSpanCount) { + logger.info("Success: Got " + expectedSpanCount + " spans."); + return true; + } + logger.info( + "Trace Found. The trace did not contain " + + expectedSpanCount + + " spans. Going to retry."); + return false; + }); + } catch (ConditionTimeoutException ignored) { + // Ignore to let assertions below run and fail with descriptive messages + } + Trace traceResp = traceRespHolder[0]; // Make sure we got as many spans as we expected. assertNotNull(traceResp); diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRule.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRule.java index ce9a226a6901..2ebf196dd104 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRule.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRule.java @@ -59,6 +59,9 @@ public void evaluate() throws Throwable { return; } catch (Throwable t) { failures.add(t); + // Sleep before retrying the test. Awaitility is not used here because we are + // retrying the entire test execution statement, which is a structural JUnit + // operation rather than a state-based assertion check. Thread.sleep(retryIntervalMillis); retryIntervalMillis *= 1.5f; } From f3f806176e396cde59f3e5e1675356b0d8be627a Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 6 Jul 2026 20:56:41 +0000 Subject: [PATCH 02/13] chore(datastore): reuse core MultipleAttemptsRule and delete local duplicate - Delete duplicate MultipleAttemptsRule and MultipleAttemptsRuleTest in Datastore. - Update AbstractITDatastoreTest to use the core MultipleAttemptsRule. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../datastore/it/AbstractITDatastoreTest.java | 1 + .../datastore/it/MultipleAttemptsRule.java | 74 ------------------- .../it/MultipleAttemptsRuleTest.java | 37 ---------- 3 files changed, 1 insertion(+), 111 deletions(-) delete mode 100644 java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRule.java delete mode 100644 java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRuleTest.java diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java index d5d9c437a4de..1cb9c5d3ae71 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java @@ -82,6 +82,7 @@ import com.google.cloud.datastore.models.ExplainMetrics; import com.google.cloud.datastore.models.ExplainOptions; import com.google.cloud.datastore.models.PlanSummary; +import com.google.cloud.testing.junit4.MultipleAttemptsRule; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Range; diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRule.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRule.java deleted file mode 100644 index 2ebf196dd104..000000000000 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRule.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.datastore.it; - -import static com.google.common.base.Preconditions.checkState; - -import java.util.ArrayList; -import java.util.List; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.MultipleFailureException; -import org.junit.runners.model.Statement; - -/** - * A JUnit rule that allows us to allow multiple attempts of a test execution before it is - * ultimately failed. When it fails, all failures will be propagated as the result of the test. - */ -public final class MultipleAttemptsRule implements TestRule { - private final long initialBackoffMillis; - private final int attemptCount; - - MultipleAttemptsRule(int attemptCount) { - this(attemptCount, 1000L); - } - - public MultipleAttemptsRule(int attemptCount, long initialBackoffMillis) { - checkState(attemptCount > 0, "attemptCount must be > 0"); - checkState(initialBackoffMillis > 0, "initialBackoffMillis must be > 0"); - this.initialBackoffMillis = initialBackoffMillis; - this.attemptCount = attemptCount; - } - - @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - List failures = new ArrayList<>(); - - long retryIntervalMillis = initialBackoffMillis; - - for (int i = 1; i <= attemptCount; i++) { - try { - base.evaluate(); - return; - } catch (Throwable t) { - failures.add(t); - // Sleep before retrying the test. Awaitility is not used here because we are - // retrying the entire test execution statement, which is a structural JUnit - // operation rather than a state-based assertion check. - Thread.sleep(retryIntervalMillis); - retryIntervalMillis *= 1.5f; - } - } - - MultipleFailureException.assertEmpty(failures); - } - }; - } -} diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRuleTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRuleTest.java deleted file mode 100644 index bb6a3c2f5593..000000000000 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/MultipleAttemptsRuleTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.datastore.it; - -import static org.junit.Assert.assertEquals; - -import org.junit.Rule; -import org.junit.Test; - -public final class MultipleAttemptsRuleTest { - - private static final int NUMBER_OF_ATTEMPTS = 5; - - @Rule public MultipleAttemptsRule rr = new MultipleAttemptsRule(NUMBER_OF_ATTEMPTS, 10); - - private int numberAttempted = 0; - - @Test - public void wontPassUntil5() { - numberAttempted += 1; - assertEquals(NUMBER_OF_ATTEMPTS, numberAttempted); - } -} From 09b3879458604d3f2a77c5c4eb4566999e416ee0 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 6 Jul 2026 21:17:41 +0000 Subject: [PATCH 03/13] style(datastore): format compliance fixes Run spotify:fmt-maven-plugin to fix CI formatting errors. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../cloud/datastore/it/AbstractITDatastoreTest.java | 5 ++--- .../com/google/cloud/datastore/it/ITE2ETracingTest.java | 9 +++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java index 1cb9c5d3ae71..2113d059f9b3 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java @@ -26,6 +26,7 @@ import static com.google.cloud.datastore.aggregation.Aggregation.sum; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; +import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -34,10 +35,7 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; -import static org.awaitility.Awaitility.await; -import java.time.Duration; -import org.awaitility.core.ConditionTimeoutException; import com.google.cloud.Timestamp; import com.google.cloud.Tuple; import com.google.cloud.datastore.AggregationQuery; @@ -103,6 +101,7 @@ import java.util.concurrent.Future; import java.util.function.BiConsumer; import java.util.function.Consumer; +import org.awaitility.core.ConditionTimeoutException; import org.junit.After; import org.junit.Before; import org.junit.Rule; diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index dfe39c37a603..873ab155ea5e 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -31,15 +31,13 @@ import static com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_RUN_QUERY; import static com.google.common.truth.Truth.assertThat; import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME; +import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.awaitility.Awaitility.await; -import java.time.Duration; -import org.awaitility.core.ConditionTimeoutException; import com.google.api.gax.core.FixedCredentialsProvider; import com.google.api.gax.rpc.DeadlineExceededException; import com.google.api.gax.rpc.NotFoundException; @@ -84,6 +82,7 @@ import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.samplers.Sampler; import java.io.IOException; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -94,6 +93,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import org.awaitility.core.ConditionTimeoutException; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -474,7 +474,8 @@ protected void fetchAndValidateTrace( + expectedSpanCount + ", retrievedSpanCount=" + retrievedTrace.getSpansCount()); - return retrievedTrace != null && expectedSpanCount == retrievedTrace.getSpansCount(); + return retrievedTrace != null + && expectedSpanCount == retrievedTrace.getSpansCount(); }); } catch (ConditionTimeoutException e) { throw new RuntimeException( From 2d4b8143348adf2f8a0b1b1d2d35f75e001a1e3d Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 6 Jul 2026 21:21:08 +0000 Subject: [PATCH 04/13] refactor(datastore): address PR comments on Awaitility migration - Replace raw arrays with AtomicReference for capturing variables in lambdas. - Correct timeout calculation in ITE2ETracingTest (use backoff * retry count instead of just retry count as seconds). TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../cloud/datastore/it/AbstractITDatastoreTest.java | 8 ++++---- .../google/cloud/datastore/it/ITE2ETracingTest.java | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java index 2113d059f9b3..1a0b62d4bcb1 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/AbstractITDatastoreTest.java @@ -99,6 +99,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import org.awaitility.core.ConditionTimeoutException; @@ -285,8 +286,7 @@ private Iterator getStronglyConsistentResults(Query scQuery, Query query) QueryResults scResults = datastore.run(scQuery); List scResultsCopy = makeResultsCopy(scResults); Set scResultsSet = new HashSet<>(scResultsCopy); - @SuppressWarnings("unchecked") - final List[] finalResults = new List[1]; + AtomicReference> finalResults = new AtomicReference<>(); try { await() .atMost(Duration.ofSeconds(10)) @@ -298,7 +298,7 @@ private Iterator getStronglyConsistentResults(Query scQuery, Query query) Set resultsSet = new HashSet<>(resultsCopy); if (scResultsSet.size() == resultsSet.size() && scResultsSet.containsAll(resultsSet)) { - finalResults[0] = resultsCopy; + finalResults.set(resultsCopy); return true; } return false; @@ -307,7 +307,7 @@ private Iterator getStronglyConsistentResults(Query scQuery, Query query) throw new RuntimeException( "reached max number of attempts to get strongly consistent results.", e); } - return finalResults[0].iterator(); + return finalResults.get().iterator(); } private List makeResultsCopy(QueryResults scResults) { diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index 873ab155ea5e..cadb4aa0757e 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -91,6 +91,7 @@ import java.util.Random; import java.util.TreeMap; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; import org.awaitility.core.ConditionTimeoutException; @@ -460,7 +461,7 @@ protected void fetchAndValidateTrace( try { await() - .atMost(Duration.ofSeconds(GET_TRACE_RETRY_COUNT)) + .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) .ignoreExceptionsInstanceOf(NotFoundException.class) .ignoreExceptionsInstanceOf(DeadlineExceededException.class) @@ -537,18 +538,18 @@ public void traceContainerTest() throws Exception { } waitForTracesToComplete(); - final Trace[] traceRespHolder = new Trace[1]; + AtomicReference traceRespHolder = new AtomicReference<>(); int expectedSpanCount = 2; try { await() - .atMost(Duration.ofSeconds(GET_TRACE_RETRY_COUNT)) + .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) .ignoreExceptionsInstanceOf(NotFoundException.class) .until( () -> { Trace trace = traceClient.getTrace(projectId, customSpanContext.getTraceId()); - traceRespHolder[0] = trace; + traceRespHolder.set(trace); if (trace.getSpansCount() == expectedSpanCount) { logger.info("Success: Got " + expectedSpanCount + " spans."); return true; @@ -562,7 +563,7 @@ public void traceContainerTest() throws Exception { } catch (ConditionTimeoutException ignored) { // Ignore to let assertions below run and fail with descriptive messages } - Trace traceResp = traceRespHolder[0]; + Trace traceResp = traceRespHolder.get(); // Make sure we got as many spans as we expected. assertNotNull(traceResp); From 73a9fe28eec5968e6ec91e6d74dbb1d155fb9c1b Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 6 Jul 2026 22:00:22 +0000 Subject: [PATCH 05/13] test(datastore): ignore StatusRuntimeException in E2E tracing tests Ignore StatusRuntimeException (which is thrown by the gRPC layer when a trace is not found yet) during Awaitility polling to prevent the tests from failing immediately before the timeout is reached. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../java/com/google/cloud/datastore/it/ITE2ETracingTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index cadb4aa0757e..d2d285f191f0 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -67,6 +67,7 @@ import com.google.devtools.cloudtrace.v1.TraceSpan; import com.google.testing.junit.testparameterinjector.TestParameter; import com.google.testing.junit.testparameterinjector.TestParameterInjector; +import io.grpc.StatusRuntimeException; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; @@ -466,6 +467,7 @@ protected void fetchAndValidateTrace( .ignoreExceptionsInstanceOf(NotFoundException.class) .ignoreExceptionsInstanceOf(DeadlineExceededException.class) .ignoreExceptionsInstanceOf(IndexOutOfBoundsException.class) + .ignoreExceptionsInstanceOf(StatusRuntimeException.class) .until( () -> { retrievedTrace = traceClient.getTrace(projectId, traceId); @@ -546,6 +548,7 @@ public void traceContainerTest() throws Exception { .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) .ignoreExceptionsInstanceOf(NotFoundException.class) + .ignoreExceptionsInstanceOf(StatusRuntimeException.class) .until( () -> { Trace trace = traceClient.getTrace(projectId, customSpanContext.getTraceId()); From 63676b535f9986541a3b87dcfbffb5f8fa7f94b5 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 6 Jul 2026 22:16:44 +0000 Subject: [PATCH 06/13] test(datastore): ignore all exceptions in E2E tracing tests Use ignoreExceptions() in Awaitility to ignore all exceptions during polling, as specific exception types (like NotFoundException and StatusRuntimeException) were still causing early failures in E2E environment. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../google/cloud/datastore/it/ITE2ETracingTest.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index d2d285f191f0..bc56f9e05b40 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -39,8 +39,6 @@ import static org.junit.Assert.assertTrue; import com.google.api.gax.core.FixedCredentialsProvider; -import com.google.api.gax.rpc.DeadlineExceededException; -import com.google.api.gax.rpc.NotFoundException; import com.google.auth.Credentials; import com.google.cloud.datastore.AggregationQuery; import com.google.cloud.datastore.AggregationResult; @@ -67,7 +65,6 @@ import com.google.devtools.cloudtrace.v1.TraceSpan; import com.google.testing.junit.testparameterinjector.TestParameter; import com.google.testing.junit.testparameterinjector.TestParameterInjector; -import io.grpc.StatusRuntimeException; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; @@ -464,10 +461,7 @@ protected void fetchAndValidateTrace( await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptionsInstanceOf(NotFoundException.class) - .ignoreExceptionsInstanceOf(DeadlineExceededException.class) - .ignoreExceptionsInstanceOf(IndexOutOfBoundsException.class) - .ignoreExceptionsInstanceOf(StatusRuntimeException.class) + .ignoreExceptions() .until( () -> { retrievedTrace = traceClient.getTrace(projectId, traceId); @@ -547,8 +541,7 @@ public void traceContainerTest() throws Exception { await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptionsInstanceOf(NotFoundException.class) - .ignoreExceptionsInstanceOf(StatusRuntimeException.class) + .ignoreExceptions() .until( () -> { Trace trace = traceClient.getTrace(projectId, customSpanContext.getTraceId()); From ddabb38b375eeb0141301c5b363d9a5d5b513ace Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 6 Jul 2026 22:18:48 +0000 Subject: [PATCH 07/13] test(datastore): use specific exception matching by name in E2E tests Use ignoreExceptionsMatching with a helper that checks exception class names as strings. This avoids classloader compatibility issues that might prevent ignoreExceptionsInstanceOf from matching correctly, while still keeping the ignored exceptions list specific. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../google/cloud/datastore/it/ITE2ETracingTest.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index bc56f9e05b40..47d7b4d0744d 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -461,7 +461,7 @@ protected void fetchAndValidateTrace( await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptions() + .ignoreExceptionsMatching(ITE2ETracingTest::isIgnoredException) .until( () -> { retrievedTrace = traceClient.getTrace(projectId, traceId); @@ -541,7 +541,7 @@ public void traceContainerTest() throws Exception { await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptions() + .ignoreExceptionsMatching(ITE2ETracingTest::isIgnoredException) .until( () -> { Trace trace = traceClient.getTrace(projectId, customSpanContext.getTraceId()); @@ -1025,4 +1025,12 @@ public void runInTransactionQueryTest() throws Exception { Arrays.asList(SPAN_NAME_TRANSACTION_RUN, SPAN_NAME_TRANSACTION_RUN_QUERY), Arrays.asList(SPAN_NAME_TRANSACTION_RUN, SPAN_NAME_TRANSACTION_COMMIT))); } + + private static boolean isIgnoredException(Throwable e) { + String name = e.getClass().getName(); + return name.equals("com.google.api.gax.rpc.NotFoundException") + || name.equals("io.grpc.StatusRuntimeException") + || name.equals("com.google.api.gax.rpc.DeadlineExceededException") + || name.equals("java.lang.IndexOutOfBoundsException"); + } } From 3521f3fce325c9828459d51cd87c925a905825a7 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 6 Jul 2026 22:24:15 +0000 Subject: [PATCH 08/13] test(datastore): use ignoreExceptionsInstanceOf with ApiException in E2E tests Revert to ignoreExceptionsInstanceOf, but use ApiException.class (parent of NotFoundException and DeadlineExceededException) and StatusRuntimeException.class to ignore the relevant exceptions during Awaitility polling. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../cloud/datastore/it/ITE2ETracingTest.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index 47d7b4d0744d..0ed9e9393677 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -39,6 +39,7 @@ import static org.junit.Assert.assertTrue; import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.api.gax.rpc.ApiException; import com.google.auth.Credentials; import com.google.cloud.datastore.AggregationQuery; import com.google.cloud.datastore.AggregationResult; @@ -65,6 +66,7 @@ import com.google.devtools.cloudtrace.v1.TraceSpan; import com.google.testing.junit.testparameterinjector.TestParameter; import com.google.testing.junit.testparameterinjector.TestParameterInjector; +import io.grpc.StatusRuntimeException; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; @@ -461,7 +463,9 @@ protected void fetchAndValidateTrace( await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptionsMatching(ITE2ETracingTest::isIgnoredException) + .ignoreExceptionsInstanceOf(ApiException.class) + .ignoreExceptionsInstanceOf(StatusRuntimeException.class) + .ignoreExceptionsInstanceOf(IndexOutOfBoundsException.class) .until( () -> { retrievedTrace = traceClient.getTrace(projectId, traceId); @@ -541,7 +545,8 @@ public void traceContainerTest() throws Exception { await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptionsMatching(ITE2ETracingTest::isIgnoredException) + .ignoreExceptionsInstanceOf(ApiException.class) + .ignoreExceptionsInstanceOf(StatusRuntimeException.class) .until( () -> { Trace trace = traceClient.getTrace(projectId, customSpanContext.getTraceId()); @@ -1025,12 +1030,4 @@ public void runInTransactionQueryTest() throws Exception { Arrays.asList(SPAN_NAME_TRANSACTION_RUN, SPAN_NAME_TRANSACTION_RUN_QUERY), Arrays.asList(SPAN_NAME_TRANSACTION_RUN, SPAN_NAME_TRANSACTION_COMMIT))); } - - private static boolean isIgnoredException(Throwable e) { - String name = e.getClass().getName(); - return name.equals("com.google.api.gax.rpc.NotFoundException") - || name.equals("io.grpc.StatusRuntimeException") - || name.equals("com.google.api.gax.rpc.DeadlineExceededException") - || name.equals("java.lang.IndexOutOfBoundsException"); - } } From effb38fd91839c3656063a823ca4ced06a246212 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Mon, 6 Jul 2026 22:49:47 +0000 Subject: [PATCH 09/13] test(datastore): add debug logging to E2E tracing tests Add try-catch blocks with detailed logging inside Awaitility lambdas to print the exact class name and ClassLoader of exceptions thrown by traceClient.getTrace. This will help diagnose why ignoreExceptionsInstanceOf is failing to ignore them. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../cloud/datastore/it/ITE2ETracingTest.java | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index 0ed9e9393677..ed6209307a81 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -468,7 +468,18 @@ protected void fetchAndValidateTrace( .ignoreExceptionsInstanceOf(IndexOutOfBoundsException.class) .until( () -> { - retrievedTrace = traceClient.getTrace(projectId, traceId); + try { + retrievedTrace = traceClient.getTrace(projectId, traceId); + } catch (Throwable t) { + logger.log( + Level.WARNING, + "DEBUG: Caught in fetchAndValidateTrace: " + + t.getClass().getName() + + " CL: " + + t.getClass().getClassLoader(), + t); + throw t; + } assertEquals(traceId, retrievedTrace.getTraceId()); logger.info( "expectedSpanCount=" @@ -549,17 +560,28 @@ public void traceContainerTest() throws Exception { .ignoreExceptionsInstanceOf(StatusRuntimeException.class) .until( () -> { - Trace trace = traceClient.getTrace(projectId, customSpanContext.getTraceId()); - traceRespHolder.set(trace); - if (trace.getSpansCount() == expectedSpanCount) { - logger.info("Success: Got " + expectedSpanCount + " spans."); - return true; + try { + Trace trace = traceClient.getTrace(projectId, customSpanContext.getTraceId()); + traceRespHolder.set(trace); + if (trace.getSpansCount() == expectedSpanCount) { + logger.info("Success: Got " + expectedSpanCount + " spans."); + return true; + } + logger.info( + "Trace Found. The trace did not contain " + + expectedSpanCount + + " spans. Going to retry."); + return false; + } catch (Throwable t) { + logger.log( + Level.WARNING, + "DEBUG: Caught in traceContainerTest: " + + t.getClass().getName() + + " CL: " + + t.getClass().getClassLoader(), + t); + throw t; } - logger.info( - "Trace Found. The trace did not contain " - + expectedSpanCount - + " spans. Going to retry."); - return false; }); } catch (ConditionTimeoutException ignored) { // Ignore to let assertions below run and fail with descriptive messages From 57f9f33dd541d0cd5cc432b79a4f8e5b745b1079 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Tue, 7 Jul 2026 01:46:53 +0000 Subject: [PATCH 10/13] test(datastore): ignore Throwable in E2E tracing tests for diagnostics Temporarily ignore Throwable.class in Awaitility to diagnose if specific exception matching is the reason why exceptions are escaping the await() block. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../com/google/cloud/datastore/it/ITE2ETracingTest.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index ed6209307a81..0b8defff6437 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -39,7 +39,6 @@ import static org.junit.Assert.assertTrue; import com.google.api.gax.core.FixedCredentialsProvider; -import com.google.api.gax.rpc.ApiException; import com.google.auth.Credentials; import com.google.cloud.datastore.AggregationQuery; import com.google.cloud.datastore.AggregationResult; @@ -66,7 +65,6 @@ import com.google.devtools.cloudtrace.v1.TraceSpan; import com.google.testing.junit.testparameterinjector.TestParameter; import com.google.testing.junit.testparameterinjector.TestParameterInjector; -import io.grpc.StatusRuntimeException; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; @@ -463,9 +461,7 @@ protected void fetchAndValidateTrace( await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptionsInstanceOf(ApiException.class) - .ignoreExceptionsInstanceOf(StatusRuntimeException.class) - .ignoreExceptionsInstanceOf(IndexOutOfBoundsException.class) + .ignoreExceptionsInstanceOf(Throwable.class) .until( () -> { try { @@ -556,8 +552,7 @@ public void traceContainerTest() throws Exception { await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptionsInstanceOf(ApiException.class) - .ignoreExceptionsInstanceOf(StatusRuntimeException.class) + .ignoreExceptionsInstanceOf(Throwable.class) .until( () -> { try { From 5f8943f84c8ef5b930005c44cb8d6dbd18db0b83 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Tue, 7 Jul 2026 02:03:56 +0000 Subject: [PATCH 11/13] test(datastore): add debug logging to Awaitility in E2E tests Add detailed logging to Awaitility's exception ignoring predicate in ITE2ETracingTest to diagnose why NotFoundException is escaping. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../cloud/datastore/it/ITE2ETracingTest.java | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index 0b8defff6437..bbaadee5f370 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -461,7 +461,20 @@ protected void fetchAndValidateTrace( await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptionsInstanceOf(Throwable.class) + .ignoreExceptionsMatching( + e -> { + logger.log( + Level.WARNING, + "DEBUG: Awaitility evaluating exception: " + + e.getClass().getName() + + " CL: " + + e.getClass().getClassLoader() + + " cause: " + + (e.getCause() != null ? e.getCause().getClass().getName() : "null")); + boolean ignore = Throwable.class.isAssignableFrom(e.getClass()); + logger.log(Level.WARNING, "DEBUG: Decision: " + ignore); + return ignore; + }) .until( () -> { try { @@ -552,7 +565,20 @@ public void traceContainerTest() throws Exception { await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptionsInstanceOf(Throwable.class) + .ignoreExceptionsMatching( + e -> { + logger.log( + Level.WARNING, + "DEBUG: Awaitility evaluating exception: " + + e.getClass().getName() + + " CL: " + + e.getClass().getClassLoader() + + " cause: " + + (e.getCause() != null ? e.getCause().getClass().getName() : "null")); + boolean ignore = Throwable.class.isAssignableFrom(e.getClass()); + logger.log(Level.WARNING, "DEBUG: Decision: " + ignore); + return ignore; + }) .until( () -> { try { From 2695dc524a13d1d7a70ab422cbe05228b151e1d7 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Tue, 7 Jul 2026 02:13:01 +0000 Subject: [PATCH 12/13] test(datastore): fix bug where SEVERE log was printed on success in E2E tests Move the logger.severe("CallStack not found in TraceContainer.") inside the if block that throws RuntimeException, so it is only logged when the call stack is actually not found. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../java/com/google/cloud/datastore/it/ITE2ETracingTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index bbaadee5f370..0d5bc84129e4 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -522,6 +522,7 @@ protected void fetchAndValidateTrace( logger.info("Checking if TraceContainer contains the callStack"); String[] expectedCallList = new String[expectedCallStack.size()]; if (!traceContainer.containsCallStack(expectedCallStack.toArray(expectedCallList))) { + logger.severe("CallStack not found in TraceContainer."); throw new RuntimeException( "Expected spans: " + Arrays.toString(expectedCallList) @@ -530,7 +531,6 @@ protected void fetchAndValidateTrace( ? retrievedTrace.getSpansList().toString() : "Trace NOT_FOUND")); } - logger.severe("CallStack not found in TraceContainer."); } } From 9b3b5411607d05535b22093d517c6bc9cc4f9fc1 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Tue, 7 Jul 2026 03:11:12 +0000 Subject: [PATCH 13/13] test(datastore): clean up debug logging in E2E tracing tests Remove temporary debug logging and revert ignoreExceptionsMatching back to ignoreExceptionsInstanceOf(Throwable.class) in ITE2ETracingTest. TAG=agy CONV=c8697e3d-c06b-4d5b-86e9-f8950e4298fd --- .../cloud/datastore/it/ITE2ETracingTest.java | 74 ++++--------------- 1 file changed, 13 insertions(+), 61 deletions(-) diff --git a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java index 0d5bc84129e4..f3cfe1a6f777 100644 --- a/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java +++ b/java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/it/ITE2ETracingTest.java @@ -461,34 +461,10 @@ protected void fetchAndValidateTrace( await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptionsMatching( - e -> { - logger.log( - Level.WARNING, - "DEBUG: Awaitility evaluating exception: " - + e.getClass().getName() - + " CL: " - + e.getClass().getClassLoader() - + " cause: " - + (e.getCause() != null ? e.getCause().getClass().getName() : "null")); - boolean ignore = Throwable.class.isAssignableFrom(e.getClass()); - logger.log(Level.WARNING, "DEBUG: Decision: " + ignore); - return ignore; - }) + .ignoreExceptionsInstanceOf(Throwable.class) .until( () -> { - try { - retrievedTrace = traceClient.getTrace(projectId, traceId); - } catch (Throwable t) { - logger.log( - Level.WARNING, - "DEBUG: Caught in fetchAndValidateTrace: " - + t.getClass().getName() - + " CL: " - + t.getClass().getClassLoader(), - t); - throw t; - } + retrievedTrace = traceClient.getTrace(projectId, traceId); assertEquals(traceId, retrievedTrace.getTraceId()); logger.info( "expectedSpanCount=" @@ -565,44 +541,20 @@ public void traceContainerTest() throws Exception { await() .atMost(Duration.ofMillis((long) GET_TRACE_RETRY_COUNT * GET_TRACE_RETRY_BACKOFF_MILLIS)) .pollInterval(Duration.ofMillis(GET_TRACE_RETRY_BACKOFF_MILLIS)) - .ignoreExceptionsMatching( - e -> { - logger.log( - Level.WARNING, - "DEBUG: Awaitility evaluating exception: " - + e.getClass().getName() - + " CL: " - + e.getClass().getClassLoader() - + " cause: " - + (e.getCause() != null ? e.getCause().getClass().getName() : "null")); - boolean ignore = Throwable.class.isAssignableFrom(e.getClass()); - logger.log(Level.WARNING, "DEBUG: Decision: " + ignore); - return ignore; - }) + .ignoreExceptionsInstanceOf(Throwable.class) .until( () -> { - try { - Trace trace = traceClient.getTrace(projectId, customSpanContext.getTraceId()); - traceRespHolder.set(trace); - if (trace.getSpansCount() == expectedSpanCount) { - logger.info("Success: Got " + expectedSpanCount + " spans."); - return true; - } - logger.info( - "Trace Found. The trace did not contain " - + expectedSpanCount - + " spans. Going to retry."); - return false; - } catch (Throwable t) { - logger.log( - Level.WARNING, - "DEBUG: Caught in traceContainerTest: " - + t.getClass().getName() - + " CL: " - + t.getClass().getClassLoader(), - t); - throw t; + Trace trace = traceClient.getTrace(projectId, customSpanContext.getTraceId()); + traceRespHolder.set(trace); + if (trace.getSpansCount() == expectedSpanCount) { + logger.info("Success: Got " + expectedSpanCount + " spans."); + return true; } + logger.info( + "Trace Found. The trace did not contain " + + expectedSpanCount + + " spans. Going to retry."); + return false; }); } catch (ConditionTimeoutException ignored) { // Ignore to let assertions below run and fail with descriptive messages