/** * Returns {@code true} if another poll operation should be made or throws {@link * CancellationException} otherwise. * * @param nextAttemptSettings attempt settings, which will be used for the next attempt, if * accepted * @return {@code true} if more attempts should be made, never returns {@code false} (throws * {@code CancellationException} instead) * @throws CancellationException if no more attempts should be made */ @Override public boolean shouldRetry(TimedAttemptSettings nextAttemptSettings) throws CancellationException { if (super.shouldRetry(nextAttemptSettings)) { return true; } throw new CancellationException(); } }
/** * Returns {@code true} if another poll operation should be made or throws {@link * CancellationException} otherwise. * * @param nextAttemptSettings attempt settings, which will be used for the next attempt, if * accepted * @return {@code true} if more attempts should be made, never returns {@code false} (throws * {@code CancellationException} instead) * @throws CancellationException if no more attempts should be made */ @Override public boolean shouldRetry(TimedAttemptSettings nextAttemptSettings) throws CancellationException { if (super.shouldRetry(nextAttemptSettings)) { return true; } throw new CancellationException(); } }
/** * Returns {@code true} if another poll operation should be made or throws {@link PollException}, * if either total timeout or total number of attempts is exceeded. * * @param nextAttemptSettings attempt settings, which will be used for the next attempt, if * accepted * @return {@code true} if more attempts should be made, never returns {@code false} (throws * {@code PollException} instead) * @throws PollException if no more attempts should be made */ @Override public boolean shouldRetry(TimedAttemptSettings nextAttemptSettings) throws PollException { if (super.shouldRetry(nextAttemptSettings)) { return true; } throw new PollException( "total timeout or maximum number of attempts exceeded; current settings: " + nextAttemptSettings.getGlobalSettings()); } }
/** * Returns {@code true} if another poll operation should be made or throws {@link PollException}, * if either total timeout or total number of attempts is exceeded. * * @param nextAttemptSettings attempt settings, which will be used for the next attempt, if * accepted * @return {@code true} if more attempts should be made, never returns {@code false} (throws * {@code PollException} instead) * @throws PollException if no more attempts should be made */ @Override public boolean shouldRetry(TimedAttemptSettings nextAttemptSettings) throws PollException { if (super.shouldRetry(nextAttemptSettings)) { return true; } throw new PollException( "total timeout or maximum number of attempts exceeded; current settings: " + nextAttemptSettings.getGlobalSettings()); } }
@Test public void testShouldRetryFalseOnMaxAttempts() { TimedAttemptSettings attempt = algorithm.createFirstAttempt(); for (int i = 0; i < 6; i++) { assertTrue(algorithm.shouldRetry(attempt)); attempt = algorithm.createNextAttempt(attempt); } assertFalse(algorithm.shouldRetry(attempt)); }
@Test public void testShouldRetryTrue() { TimedAttemptSettings attempt = algorithm.createFirstAttempt(); for (int i = 0; i < 2; i++) { attempt = algorithm.createNextAttempt(attempt); } assertTrue(algorithm.shouldRetry(attempt)); }
@Test public void testShouldRetryFalseOnMaxTimeout() { TimedAttemptSettings attempt = algorithm.createFirstAttempt(); for (int i = 0; i < 4; i++) { assertTrue(algorithm.shouldRetry(attempt)); attempt = algorithm.createNextAttempt(attempt); clock.incrementNanoTime(Duration.ofMillis(50L).toNanos()); } assertFalse(algorithm.shouldRetry(attempt)); } }
protected Long getNextBackoff() { if (currentBackoff == null) { // Historically, the client waited for "total timeout" after the first failure. For now, // that behavior is preserved, even though that's not the ideal. // // TODO: Think through retries, and create policy that works with the mental model most // users would have of relating to retries. That would likely involve updating some // default settings in addition to changing the algorithm. currentBackoff = exponentialRetryAlgorithm.createFirstAttempt(); } currentBackoff = exponentialRetryAlgorithm.createNextAttempt(currentBackoff); if (!exponentialRetryAlgorithm.shouldRetry(currentBackoff)) { // TODO: consider creating a subclass of exponentialRetryAlgorithm to encapsulate this logic long timeLeftNs = currentBackoff.getGlobalSettings().getTotalTimeout().toNanos() - (clock.nanoTime() - currentBackoff.getFirstAttemptStartTimeNanos()); long timeLeftMs = TimeUnit.NANOSECONDS.toMillis(timeLeftNs); if (timeLeftMs > currentBackoff.getGlobalSettings().getInitialRetryDelay().toMillis()) { // The backoff algorithm doesn't always wait until the timeout is achieved. Wait // one final time so that retries hit return timeLeftMs; } else { // Finish for real. return null; } } else { return currentBackoff.getRetryDelay().toMillis(); } }