throw new IllegalArgumentException("Tick duration must not be negative"); long tickNanos = tickDuration.toNanos(); if (tickNanos % 1000000 == 0) {
throw new IllegalArgumentException("Tick duration must not be negative"); long tickNanos = tickDuration.toNanos(); if (tickNanos % 1000000 == 0) {
/** * Returns {@code true} if another attempt should be made, or {@code false} otherwise. * * @param nextAttemptSettings attempt settings, which will be used for the next attempt, if * accepted * @return {@code true} if {@code nextAttemptSettings} does not exceed either maxAttempts limit or * totalTimeout limit, or {@code false} otherwise */ @Override public boolean shouldRetry(TimedAttemptSettings nextAttemptSettings) { RetrySettings globalSettings = nextAttemptSettings.getGlobalSettings(); long totalTimeSpentNanos = clock.nanoTime() - nextAttemptSettings.getFirstAttemptStartTimeNanos() + nextAttemptSettings.getRandomizedRetryDelay().toNanos(); return totalTimeSpentNanos <= globalSettings.getTotalTimeout().toNanos() && (globalSettings.getMaxAttempts() <= 0 || nextAttemptSettings.getAttemptCount() < globalSettings.getMaxAttempts()); }
/** * Returns {@code true} if another attempt should be made, or {@code false} otherwise. * * @param nextAttemptSettings attempt settings, which will be used for the next attempt, if * accepted * @return {@code true} if {@code nextAttemptSettings} does not exceed either maxAttempts limit or * totalTimeout limit, or {@code false} otherwise */ @Override public boolean shouldRetry(TimedAttemptSettings nextAttemptSettings) { RetrySettings globalSettings = nextAttemptSettings.getGlobalSettings(); long totalTimeSpentNanos = clock.nanoTime() - nextAttemptSettings.getFirstAttemptStartTimeNanos() + nextAttemptSettings.getRandomizedRetryDelay().toNanos(); return totalTimeSpentNanos <= globalSettings.getTotalTimeout().toNanos() && (globalSettings.getMaxAttempts() <= 0 || nextAttemptSettings.getAttemptCount() < globalSettings.getMaxAttempts()); }
throw new DateTimeException("Unit is too large to be used for truncation"); long dur = unitDur.toNanos(); if ((LocalTime.NANOS_PER_DAY % dur) != 0) { throw new DateTimeException("Unit must divide into a standard day without remainder");
throw new DateTimeException("Unit is too large to be used for truncation"); long dur = unitDur.toNanos(); if ((NANOS_PER_DAY % dur) != 0) { throw new DateTimeException("Unit must divide into a standard day without remainder");
throw new DateTimeException("Unit is too large to be used for truncation"); long dur = unitDur.toNanos(); if ((NANOS_PER_DAY % dur) != 0) { throw new DateTimeException("Unit must divide into a standard day without remainder");
throw new DateTimeException("Unit is too large to be used for truncation"); long dur = unitDur.toNanos(); if ((LocalTime.NANOS_PER_DAY % dur) != 0) { throw new DateTimeException("Unit must divide into a standard day without remainder");
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(); } }
@Test public void testIdleTimeout() throws InterruptedException { clock.incrementNanoTime(idleTime.toNanos() - 1); watchdog.run(); Truth.assertThat(call.getController().isCancelled()).isFalse(); clock.incrementNanoTime(1); watchdog.run(); Truth.assertThat(call.getController().isCancelled()).isTrue(); call.getController() .getObserver() .onError(new RuntimeException("Some upstream exception representing cancellation")); Throwable actualError = null; try { innerObserver.done.get(); } catch (ExecutionException t) { actualError = t.getCause(); } Truth.assertThat(actualError).isInstanceOf(WatchdogTimeoutException.class); }
@Test public void testWaitTimeout() throws Exception { innerObserver.controller.get(1, TimeUnit.MILLISECONDS).request(1); clock.incrementNanoTime(waitTime.toNanos() - 1); watchdog.run(); Truth.assertThat(call.getController().isCancelled()).isFalse(); clock.incrementNanoTime(1); watchdog.run(); Truth.assertThat(call.getController().isCancelled()).isTrue(); call.getController() .getObserver() .onError(new RuntimeException("Some upstream exception representing cancellation")); Throwable actualError = null; try { innerObserver.done.get(); } catch (ExecutionException t) { actualError = t.getCause(); } Truth.assertThat(actualError).isInstanceOf(WatchdogTimeoutException.class); }
@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)); } }
clock.incrementNanoTime(waitTime.toNanos()); call1.getController().getObserver().onResponse("resp1");