Skip to content

Commit efeaff9

Browse files
authored
test(gax): disable jitter in CancellationTest SLOW_RETRY_SETTINGS (#14702)
Fixes #14539
1 parent 725f47a commit efeaff9

1 file changed

Lines changed: 19 additions & 1 deletion

File tree

‎sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/rpc/CancellationTest.java‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ class CancellationTest {
6969
.setTotalTimeoutDuration(java.time.Duration.ofMillis(20L))
7070
.build();
7171

72+
// Disable jitter so ExponentialRetryAlgorithm.createNextAttempt always schedules the full
73+
// 3000ms retry delay instead of picking a random delay in [0, 3000) ms via
74+
// ThreadLocalRandom.current().nextLong(bound), which can otherwise expire before the
75+
// cancellation thread calls resultFuture.cancel(true).
76+
// totalTimeoutDuration must exceed initialRetryDelayDuration (10000ms > 3000ms) because
77+
// ExponentialRetryAlgorithm.shouldRetry checks (totalTimeout - timeElapsed - retryDelay) > 0
78+
// before scheduling the next attempt.
7279
private static final RetrySettings SLOW_RETRY_SETTINGS =
7380
RetrySettings.newBuilder()
7481
.setInitialRetryDelayDuration(java.time.Duration.ofMillis(3000L))
@@ -77,7 +84,8 @@ class CancellationTest {
7784
.setInitialRpcTimeoutDuration(java.time.Duration.ofMillis(3000L))
7885
.setRpcTimeoutMultiplier(1)
7986
.setMaxRpcTimeoutDuration(java.time.Duration.ofMillis(3000L))
80-
.setTotalTimeoutDuration(java.time.Duration.ofMillis(3000L))
87+
.setTotalTimeoutDuration(java.time.Duration.ofMillis(10000L))
88+
.setJittered(false)
8189
.build();
8290

8391
private FakeApiClock fakeClock;
@@ -197,6 +205,10 @@ void cancellationDuringRetryDelay() throws Exception {
197205
Throwable throwable =
198206
new UnavailableException(null, FakeStatusCode.of(StatusCode.Code.UNAVAILABLE), true);
199207
CancellationTrackingFuture<Integer> innerFuture = CancellationTrackingFuture.create();
208+
// Attempt 1 fails immediately with UNAVAILABLE, causing CallbackChainRetryingFuture to
209+
// schedule Attempt 2 on the executor with SLOW_RETRY_SETTINGS's retry delay.
210+
// innerFuture is queued in Mockito for Attempt 2 and is only returned if the retry delay
211+
// expires and AttemptCallable.call() invokes callInt.futureCall() a second time.
200212
Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
201213
.thenReturn(RetryingTest.<Integer>immediateFailedFuture(throwable))
202214
.thenReturn(innerFuture);
@@ -220,6 +232,12 @@ void cancellationDuringRetryDelay() throws Exception {
220232
Truth.assertThat(gotException).isNotNull();
221233
Truth.assertThat(resultFuture.isDone()).isTrue();
222234
Truth.assertThat(resultFuture.isCancelled()).isTrue();
235+
// CallbackChainRetryingFuture replaces its active attemptFuture at each step of the retry
236+
// loop (Attempt 1 future -> scheduled retry-delay timer future -> Attempt 2 future via
237+
// AttemptCallable.call()). While waiting in the retry delay, attemptFuture points to the
238+
// scheduled timer future from ScheduledRetryingExecutor.submit(). Cancelling resultFuture
239+
// during this delay cancels that timer task before AttemptCallable.call() runs Attempt 2,
240+
// so callInt.futureCall() is never called a second time and innerFuture is never cancelled.
223241
Truth.assertThat(innerFuture.isCancelled()).isFalse();
224242

225243
scheduler.shutdownNow();

0 commit comments

Comments
 (0)