Skip to content

Commit a2ee512

Browse files
authored
chore: Fix testCancelOuterFutureBeforeStart flaky test (#1583)
* chore: Fix testCancelOuterFutureBeforeStart flaky test * chore: Fix assertions * chore: Allow FailableCallable to have access to ExternalFuture * chore: Fix format issues * chore: Use MockCallable for this flaky test * chore: Update comment * chore: Update to the latest mockito version * chore: Address PR comments * chore: Add externalfuture for FailingCallable * chore: Add comments to document the externalFuture * chore: Move tracer verification to the end
1 parent 59efbbb commit a2ee512

File tree

3 files changed

+38
-6
lines changed

3 files changed

+38
-6
lines changed

gax-java/gax/src/test/java/com/google/api/gax/retrying/AbstractRetryingExecutorTest.java

+15-6
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ public void testSuccess() throws Exception {
120120
RetryingExecutorWithContext<String> executor =
121121
getExecutor(getAlgorithm(getDefaultRetrySettings(), 0, null));
122122
RetryingFuture<String> future = executor.createFuture(callable, retryingContext);
123+
callable.setExternalFuture(future);
123124
future.setAttemptFuture(executor.submit(future));
124125

125126
assertFutureSuccess(future);
@@ -136,6 +137,7 @@ public void testSuccessWithFailures() throws Exception {
136137
RetryingExecutorWithContext<String> executor =
137138
getExecutor(getAlgorithm(getDefaultRetrySettings(), 0, null));
138139
RetryingFuture<String> future = executor.createFuture(callable, retryingContext);
140+
callable.setExternalFuture(future);
139141
future.setAttemptFuture(executor.submit(future));
140142

141143
assertFutureSuccess(future);
@@ -153,6 +155,7 @@ public void testSuccessWithFailuresPeekGetAttempt() throws Exception {
153155
RetryingExecutorWithContext<String> executor =
154156
getExecutor(getAlgorithm(getDefaultRetrySettings(), 0, null));
155157
RetryingFuture<String> future = executor.createFuture(callable, retryingContext);
158+
callable.setExternalFuture(future);
156159

157160
assertNull(future.peekAttemptResult());
158161
assertSame(future.peekAttemptResult(), future.peekAttemptResult());
@@ -179,6 +182,7 @@ public void testMaxRetriesExceeded() throws Exception {
179182
RetryingExecutorWithContext<String> executor =
180183
getExecutor(getAlgorithm(getDefaultRetrySettings(), 0, null));
181184
RetryingFuture<String> future = executor.createFuture(callable, retryingContext);
185+
callable.setExternalFuture(future);
182186
future.setAttemptFuture(executor.submit(future));
183187

184188
assertFutureFail(future, CustomException.class);
@@ -211,6 +215,7 @@ public void testTotalTimeoutExceeded() throws Exception {
211215
context = FakeCallContext.createDefault().withTracer(tracer);
212216
}
213217
RetryingFuture<String> future = executor.createFuture(callable, context);
218+
callable.setExternalFuture(future);
214219
future.setAttemptFuture(executor.submit(future));
215220

216221
assertFutureFail(future, CustomException.class);
@@ -222,28 +227,29 @@ public void testTotalTimeoutExceeded() throws Exception {
222227
}
223228

224229
@Test
225-
public void testCancelOuterFutureBeforeStart() throws Exception {
226-
FailingCallable callable = new FailingCallable(4, "request", "SUCCESS", tracer);
230+
public void testCancelOuterFutureBeforeStart() {
231+
FailingCallable callable = new FailingCallable(0, "request", "FAILURE", tracer);
227232

228233
RetrySettings retrySettings =
229234
FAST_RETRY_SETTINGS
230235
.toBuilder()
231236
.setInitialRetryDelay(Duration.ofMillis(1_000L))
232237
.setMaxRetryDelay(Duration.ofMillis(1_000L))
233-
.setTotalTimeout(Duration.ofMillis(10_0000L))
238+
.setTotalTimeout(Duration.ofMillis(10_000L))
234239
.build();
235240
RetryingExecutorWithContext<String> executor =
236241
getExecutor(getAlgorithm(retrySettings, 0, null));
237242
RetryingFuture<String> future = executor.createFuture(callable, retryingContext);
243+
callable.setExternalFuture(future);
238244
boolean res = future.cancel(false);
239-
240245
assertTrue(res);
246+
assertTrue(future.isCancelled());
241247

242248
future.setAttemptFuture(executor.submit(future));
243-
244-
assertFutureCancel(future);
245249
assertEquals(0, future.getAttemptSettings().getAttemptCount());
246250

251+
// Tracer should have 0 interactions as the externalFuture has already been cancelled
252+
// FailingCallable should exit immediately and no tracer logic should be invoked
247253
verifyNoMoreInteractions(tracer);
248254
}
249255

@@ -253,6 +259,7 @@ public void testCancelByRetryingAlgorithm() throws Exception {
253259
RetryingExecutorWithContext<String> executor =
254260
getExecutor(getAlgorithm(getDefaultRetrySettings(), 5, new CancellationException()));
255261
RetryingFuture<String> future = executor.createFuture(callable, retryingContext);
262+
callable.setExternalFuture(future);
256263
future.setAttemptFuture(executor.submit(future));
257264

258265
assertFutureCancel(future);
@@ -272,6 +279,7 @@ public void testUnexpectedExceptionFromRetryAlgorithm() throws Exception {
272279
RetryingExecutorWithContext<String> executor =
273280
getExecutor(getAlgorithm(getDefaultRetrySettings(), 5, new RuntimeException()));
274281
RetryingFuture<String> future = executor.createFuture(callable, retryingContext);
282+
callable.setExternalFuture(future);
275283
future.setAttemptFuture(executor.submit(future));
276284

277285
assertFutureFail(future, RuntimeException.class);
@@ -311,6 +319,7 @@ public void testPollExceptionByPollAlgorithm() throws Exception {
311319
context = FakeCallContext.createDefault().withTracer(tracer);
312320
}
313321
RetryingFuture<String> future = executor.createFuture(callable, context);
322+
callable.setExternalFuture(future);
314323
future.setAttemptFuture(executor.submit(future));
315324

316325
assertFutureFail(future, PollException.class);

gax-java/gax/src/test/java/com/google/api/gax/retrying/FailingCallable.java

+17
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
package com.google.api.gax.retrying;
3131

3232
import com.google.api.gax.tracing.ApiTracer;
33+
import com.google.common.base.Preconditions;
3334
import java.util.concurrent.Callable;
3435
import java.util.concurrent.CountDownLatch;
3536
import java.util.concurrent.atomic.AtomicInteger;
@@ -66,20 +67,36 @@ class FailingCallable implements Callable<String> {
6667
private final String result;
6768
private final CountDownLatch firstAttemptFinished = new CountDownLatch(1);
6869

70+
private volatile RetryingFuture<String> externalFuture;
71+
6972
FailingCallable(int expectedFailuresCount, String request, String result, ApiTracer tracer) {
7073
this.request = request;
7174
this.tracer = tracer;
7275
this.expectedFailuresCount = expectedFailuresCount;
7376
this.result = result;
7477
}
7578

79+
// ExternalFuture should always be passed in when using the FailingCallable
80+
// to accurately mimic the behavior of AttemptCallable. We use the external
81+
// future to check that the future is done and that none of the callable's
82+
// logic is run.
83+
public void setExternalFuture(RetryingFuture<String> externalFuture) {
84+
this.externalFuture = Preconditions.checkNotNull(externalFuture);
85+
}
86+
7687
CountDownLatch getFirstAttemptFinishedLatch() {
7788
return firstAttemptFinished;
7889
}
7990

8091
@Override
8192
public String call() throws Exception {
8293
try {
94+
// Assumption is that externalFuture is always passed in.
95+
// No null check to confirm so that the test will fail if
96+
// the externalFuture is not passed in
97+
if (externalFuture.isDone()) {
98+
return null;
99+
}
83100
int attemptNumber = attemptsCount.getAndIncrement();
84101

85102
tracer.attemptStarted(request, attemptNumber);

gax-java/gax/src/test/java/com/google/api/gax/retrying/ScheduledRetryingExecutorTest.java

+6
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public void testSuccessWithFailuresPeekAttempt() throws Exception {
101101
getRetryingExecutor(getAlgorithm(retrySettings, 0, null), localExecutor);
102102
RetryingFuture<String> future =
103103
executor.createFuture(callable, FakeCallContext.createDefault().withTracer(tracer));
104+
callable.setExternalFuture(future);
104105

105106
assertNull(future.peekAttemptResult());
106107
assertSame(future.peekAttemptResult(), future.peekAttemptResult());
@@ -151,6 +152,7 @@ public void testSuccessWithFailuresGetAttempt() throws Exception {
151152
getRetryingExecutor(getAlgorithm(retrySettings, 0, null), localExecutor);
152153
RetryingFuture<String> future =
153154
executor.createFuture(callable, FakeCallContext.createDefault().withTracer(tracer));
155+
callable.setExternalFuture(future);
154156

155157
assertNull(future.peekAttemptResult());
156158
assertSame(future.getAttemptResult(), future.getAttemptResult());
@@ -203,6 +205,7 @@ public void testCancelGetAttempt() throws Exception {
203205
RetryingExecutorWithContext<String> executor =
204206
getRetryingExecutor(getAlgorithm(retrySettings, 0, null), localExecutor);
205207
RetryingFuture<String> future = executor.createFuture(callable, retryingContext);
208+
callable.setExternalFuture(future);
206209

207210
assertNull(future.peekAttemptResult());
208211
assertSame(future.getAttemptResult(), future.getAttemptResult());
@@ -261,6 +264,7 @@ public void testCancelOuterFutureAfterStart() throws Exception {
261264
getRetryingExecutor(getAlgorithm(retrySettings, 0, null), localExecutor);
262265
RetryingFuture<String> future =
263266
executor.createFuture(callable, FakeCallContext.createDefault().withTracer(tracer));
267+
callable.setExternalFuture(future);
264268
future.setAttemptFuture(executor.submit(future));
265269

266270
Thread.sleep(30L);
@@ -288,6 +292,7 @@ public void testCancelIsTraced() throws Exception {
288292
getRetryingExecutor(getAlgorithm(retrySettings, 0, null), localExecutor);
289293
RetryingFuture<String> future =
290294
executor.createFuture(callable, FakeCallContext.createDefault().withTracer(tracer));
295+
callable.setExternalFuture(future);
291296
future.setAttemptFuture(executor.submit(future));
292297

293298
Thread.sleep(30L);
@@ -317,6 +322,7 @@ public void testCancelProxiedFutureAfterStart() throws Exception {
317322
getRetryingExecutor(getAlgorithm(retrySettings, 0, null), localExecutor);
318323
RetryingFuture<String> future =
319324
executor.createFuture(callable, FakeCallContext.createDefault().withTracer(tracer));
325+
callable.setExternalFuture(future);
320326
future.setAttemptFuture(executor.submit(future));
321327

322328
Thread.sleep(50L);

0 commit comments

Comments
 (0)