@Override public String call() throws Exception { int attemptNumber = attemptsCount.getAndIncrement(); tracer.attemptStarted(attemptNumber); if (attemptNumber < expectedFailuresCount) { throw new CustomException(); } return result; }
@Test public void testMaxRetriesExceeded() throws Exception { FailingCallable callable = new FailingCallable(6, "FAILURE", tracer); RetryingExecutorWithContext<String> executor = getExecutor(getAlgorithm(FAST_RETRY_SETTINGS, 0, null)); RetryingFuture<String> future = executor.createFuture(callable, retryingContext); future.setAttemptFuture(executor.submit(future)); assertFutureFail(future, CustomException.class); assertEquals(5, future.getAttemptSettings().getAttemptCount()); verify(tracer, times(6)).attemptStarted(anyInt()); verify(tracer, times(5)).attemptFailed(any(Throwable.class), any(Duration.class)); verify(tracer, times(1)).attemptFailedRetriesExhausted(any(Throwable.class)); verifyNoMoreInteractions(tracer); }
@Test public void testCancelByRetryingAlgorithm() throws Exception { FailingCallable callable = new FailingCallable(6, "FAILURE", tracer); RetryingExecutorWithContext<String> executor = getExecutor(getAlgorithm(FAST_RETRY_SETTINGS, 5, new CancellationException())); RetryingFuture<String> future = executor.createFuture(callable, retryingContext); future.setAttemptFuture(executor.submit(future)); assertFutureCancel(future); assertEquals(4, future.getAttemptSettings().getAttemptCount()); verify(tracer, times(5)).attemptStarted(anyInt()); // Pre-apocalypse failures verify(tracer, times(4)).attemptFailed(any(Throwable.class), any(Duration.class)); // Apocalypse failure verify(tracer, times(1)).attemptFailedRetriesExhausted(any(CancellationException.class)); verifyNoMoreInteractions(tracer); }
@Test public void testUnexpectedExceptionFromRetryAlgorithm() throws Exception { FailingCallable callable = new FailingCallable(6, "FAILURE", tracer); RetryingExecutorWithContext<String> executor = getExecutor(getAlgorithm(FAST_RETRY_SETTINGS, 5, new RuntimeException())); RetryingFuture<String> future = executor.createFuture(callable, retryingContext); future.setAttemptFuture(executor.submit(future)); assertFutureFail(future, RuntimeException.class); assertEquals(4, future.getAttemptSettings().getAttemptCount()); verify(tracer, times(5)).attemptStarted(anyInt()); // Pre-apocalypse failures verify(tracer, times(4)).attemptFailed(any(Throwable.class), any(Duration.class)); // Apocalypse failure verify(tracer, times(1)).attemptPermanentFailure(any(RuntimeException.class)); verifyNoMoreInteractions(tracer); }
@Test public void testSuccessWithFailures() throws Exception { FailingCallable callable = new FailingCallable(5, "SUCCESS", tracer); RetryingExecutorWithContext<String> executor = getExecutor(getAlgorithm(FAST_RETRY_SETTINGS, 0, null)); RetryingFuture<String> future = executor.createFuture(callable, retryingContext); future.setAttemptFuture(executor.submit(future)); assertFutureSuccess(future); assertEquals(5, future.getAttemptSettings().getAttemptCount()); verify(tracer, times(6)).attemptStarted(anyInt()); verify(tracer, times(5)).attemptFailed(any(Throwable.class), any(Duration.class)); verify(tracer, times(1)).attemptSucceeded(); verifyNoMoreInteractions(tracer); }
@Test public void testSuccess() throws Exception { FailingCallable callable = new FailingCallable(0, "SUCCESS", tracer); RetryingExecutorWithContext<String> executor = getExecutor(getAlgorithm(FAST_RETRY_SETTINGS, 0, null)); RetryingFuture<String> future = executor.createFuture(callable, retryingContext); future.setAttemptFuture(executor.submit(future)); assertFutureSuccess(future); assertEquals(0, future.getAttemptSettings().getAttemptCount()); verify(tracer, times(1)).attemptStarted(0); verify(tracer, times(1)).attemptSucceeded(); verifyNoMoreInteractions(tracer); }
.attemptStarted(outerRetryingFuture.getAttemptSettings().getOverallAttemptCount());
.attemptStarted(outerRetryingFuture.getAttemptSettings().getOverallAttemptCount());
@Test public void testTotalTimeoutExceeded() throws Exception { RetrySettings retrySettings = FAST_RETRY_SETTINGS .toBuilder() .setInitialRetryDelay(Duration.ofMillis(Integer.MAX_VALUE)) .setMaxRetryDelay(Duration.ofMillis(Integer.MAX_VALUE)) .build(); RetryingExecutorWithContext<String> executor = getExecutor(getAlgorithm(retrySettings, 0, null)); FailingCallable callable = new FailingCallable(6, "FAILURE", tracer); RetryingFuture<String> future = executor.createFuture(callable, retryingContext); future.setAttemptFuture(executor.submit(future)); assertFutureFail(future, CustomException.class); assertTrue(future.getAttemptSettings().getAttemptCount() < 4); verify(tracer, times(1)).attemptStarted(anyInt()); verify(tracer, times(1)).attemptFailedRetriesExhausted(any(Throwable.class)); verifyNoMoreInteractions(tracer); }
@Test public void testPollExceptionByPollAlgorithm() throws Exception { RetrySettings retrySettings = FAST_RETRY_SETTINGS .toBuilder() .setInitialRetryDelay(Duration.ofMillis(Integer.MAX_VALUE)) .setMaxRetryDelay(Duration.ofMillis(Integer.MAX_VALUE)) .build(); RetryAlgorithm<String> retryAlgorithm = new RetryAlgorithm<>( new TestResultRetryAlgorithm<String>(0, null), new ExponentialPollAlgorithm(retrySettings, NanoClock.getDefaultClock())); RetryingExecutorWithContext<String> executor = getExecutor(retryAlgorithm); FailingCallable callable = new FailingCallable(6, "FAILURE", tracer); RetryingFuture<String> future = executor.createFuture(callable, retryingContext); future.setAttemptFuture(executor.submit(future)); assertFutureFail(future, PollException.class); assertTrue(future.getAttemptSettings().getAttemptCount() < 4); verify(tracer, times(1)).attemptStarted(anyInt()); verify(tracer, times(1)).attemptPermanentFailure(any(PollException.class)); verifyNoMoreInteractions(tracer); }
@Override public ResponseT call() { ApiCallContext callContext = originalCallContext; try { Duration rpcTimeout = externalFuture.getAttemptSettings().getRpcTimeout(); if (!rpcTimeout.isZero()) { callContext = callContext.withTimeout(rpcTimeout); } externalFuture.setAttemptFuture(new NonCancellableFuture<ResponseT>()); if (externalFuture.isDone()) { return null; } callContext .getTracer() .attemptStarted(externalFuture.getAttemptSettings().getOverallAttemptCount()); ApiFuture<ResponseT> internalFuture = callable.futureCall(request, callContext); externalFuture.setAttemptFuture(internalFuture); } catch (Throwable e) { externalFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e)); } return null; } }
@Override public ResponseT call() { ApiCallContext callContext = originalCallContext; try { Duration rpcTimeout = externalFuture.getAttemptSettings().getRpcTimeout(); if (!rpcTimeout.isZero()) { callContext = callContext.withTimeout(rpcTimeout); } externalFuture.setAttemptFuture(new NonCancellableFuture<ResponseT>()); if (externalFuture.isDone()) { return null; } callContext .getTracer() .attemptStarted(externalFuture.getAttemptSettings().getOverallAttemptCount()); ApiFuture<ResponseT> internalFuture = callable.futureCall(request, callContext); externalFuture.setAttemptFuture(internalFuture); } catch (Throwable e) { externalFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e)); } return null; } }
.attemptStarted(externalFuture.getAttemptSettings().getOverallAttemptCount());
.attemptStarted(externalFuture.getAttemptSettings().getOverallAttemptCount());