@Override public ResponseT call() { try { // NOTE: unlike gax's AttemptCallable, this ignores rpc timeouts externalFuture.setAttemptFuture(new NonCancellableFuture<ResponseT>()); if (externalFuture.isDone()) { return null; } ApiFuture<ResponseT> internalFuture = callable.futureCall(request, callContext); externalFuture.setAttemptFuture(internalFuture); } catch (Throwable e) { externalFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e)); } return null; } }
externalFuture.setAttemptFuture(new NonCancellableFuture<Void>()); if (externalFuture.isDone()) { return null; externalFuture.setAttemptFuture(transformed); } catch (Throwable e) { externalFuture.setAttemptFuture(ApiFutures.<Void>immediateFailedFuture(e));
@Override public ResponseT call() { try { // NOTE: unlike gax's AttemptCallable, this ignores rpc timeouts externalFuture.setAttemptFuture(new NonCancellableFuture<ResponseT>()); if (externalFuture.isDone()) { return null; } ApiFuture<ResponseT> internalFuture = callable.futureCall(request, callContext); externalFuture.setAttemptFuture(internalFuture); } catch (Throwable e) { externalFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e)); } return null; } }
/** * Submits an attempt for execution in the current thread, causing the current thread to sleep for * the specified by the {@link RetryingFuture#getAttemptSettings()} amount of time. As result, * this method completes execution only after the specified {@code retryingFuture} completes. * * @param retryingFuture the future previously returned by {@link #createFuture(Callable, * RetryingContext)} * @return returns completed {@code retryingFuture} */ @Override public ApiFuture<ResponseT> submit(RetryingFuture<ResponseT> retryingFuture) { while (!retryingFuture.isDone()) { try { sleep(retryingFuture.getAttemptSettings().getRandomizedRetryDelay()); ResponseT response = retryingFuture.getCallable().call(); retryingFuture.setAttemptFuture(ApiFutures.immediateFuture(response)); } catch (InterruptedException | InterruptedIOException | ClosedByInterruptException e) { Thread.currentThread().interrupt(); retryingFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e)); } catch (Exception e) { retryingFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e)); } } return retryingFuture; }
/** * Submits an attempt for execution in the current thread, causing the current thread to sleep for * the specified by the {@link RetryingFuture#getAttemptSettings()} amount of time. As result, * this method completes execution only after the specified {@code retryingFuture} completes. * * @param retryingFuture the future previously returned by {@link #createFuture(Callable, * RetryingContext)} * @return returns completed {@code retryingFuture} */ @Override public ApiFuture<ResponseT> submit(RetryingFuture<ResponseT> retryingFuture) { while (!retryingFuture.isDone()) { try { sleep(retryingFuture.getAttemptSettings().getRandomizedRetryDelay()); ResponseT response = retryingFuture.getCallable().call(); retryingFuture.setAttemptFuture(ApiFutures.immediateFuture(response)); } catch (InterruptedException | InterruptedIOException | ClosedByInterruptException e) { Thread.currentThread().interrupt(); retryingFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e)); } catch (Exception e) { retryingFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e)); } } return retryingFuture; }
@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; } }
externalFuture.setAttemptFuture(new NonCancellableFuture<Void>()); if (externalFuture.isDone()) { return null; externalFuture.setAttemptFuture(transformed); } catch (Throwable e) { externalFuture.setAttemptFuture(ApiFutures.<Void>immediateFailedFuture(e));
externalFuture.setAttemptFuture(new NonCancellableFuture<ResponseT>()); if (externalFuture.isDone()) { return null; externalFuture.setAttemptFuture(internalFuture); } catch (Throwable e) { externalFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e));
externalFuture.setAttemptFuture(new NonCancellableFuture<ResponseT>()); if (externalFuture.isDone()) { return null; externalFuture.setAttemptFuture(internalFuture); } catch (Throwable e) { externalFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e));
@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); }
@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); }
assertFalse(future.getAttemptResult().isCancelled()); future.setAttemptFuture(executor.submit(future));
assertFalse(future.getAttemptResult().isCancelled()); future.setAttemptFuture(executor.submit(future));
@Test public void testSuccessWithFailuresPeekGetAttempt() 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); assertNull(future.peekAttemptResult()); assertSame(future.peekAttemptResult(), future.peekAttemptResult()); assertFalse(future.getAttemptResult().isDone()); assertFalse(future.getAttemptResult().isCancelled()); Exception exception = null; try { future.get(1L, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { exception = e; } assertNotNull(exception); future.setAttemptFuture(executor.submit(future)); assertFutureSuccess(future); assertEquals(5, future.getAttemptSettings().getAttemptCount()); }