DelayedResponseLevelRetryHandler implements ServiceUnavailableRetryStrategy { @Override public boolean retryRequest(HttpResponse response, int executionCount, HttpContext ctx) { log.fine(() -> String.format("retryRequest(responseCode='%s', executionCount='%d', ctx='%s'", response.getStatusLine().getStatusCode(), executionCount, ctx)); HttpClientContext clientCtx = HttpClientContext.adapt(ctx); if (!predicate.test(response, clientCtx)) { log.fine(() -> String.format("Not retrying for '%s'", ctx)); return false; } if (executionCount > maxRetries) { log.fine(() -> String.format("Max retries exceeded for '%s'", ctx)); retryFailedConsumer.onRetryFailed(response, executionCount, clientCtx); return false; } Duration delay = delaySupplier.getDelay(executionCount); log.fine(() -> String.format("Retrying after %s for '%s'", delay, ctx)); retryInterval.set(delay.toMillis()); retryConsumer.onRetry(response, delay, executionCount, clientCtx); return true; } private  DelayedResponseLevelRetryHandler(
            DelaySupplier delaySupplier,
            int maxRetries,
            RetryPredicate<HttpResponse> predicate,
            RetryConsumer<HttpResponse> retryConsumer,
            RetryFailedConsumer<HttpResponse> retryFailedConsumer); @Override boolean retryRequest(HttpResponse response, int executionCount, HttpContext ctx); @Override long getRetryInterval();  }
@Test @SuppressWarnings("unchecked") public void retry_consumers_are_invoked() { RetryConsumer<HttpResponse> retryConsumer = mock(RetryConsumer.class); RetryFailedConsumer<HttpResponse> retryFailedConsumer = mock(RetryFailedConsumer.class); Duration delay = Duration.ofSeconds(10); int maxRetries = 5; DelayedResponseLevelRetryHandler handler = DelayedResponseLevelRetryHandler.Builder .withFixedDelay(delay, maxRetries) .onRetry(retryConsumer) .onRetryFailed(retryFailedConsumer) .build(); HttpResponse response = createResponse(HttpStatus.SC_SERVICE_UNAVAILABLE); HttpClientContext ctx = new HttpClientContext(); int lastExecutionCount = maxRetries + 1; for (int i = 1; i <= lastExecutionCount; i++) { handler.retryRequest(response, i, ctx); } verify(retryFailedConsumer).onRetryFailed(response, lastExecutionCount, ctx); for (int i = 1; i < lastExecutionCount; i++) { verify(retryConsumer).onRetry(response, delay, i, ctx); } }
@Test public void retries_for_listed_exceptions_until_max_retries_exceeded() { int maxRetries = 2; DelayedResponseLevelRetryHandler handler = DelayedResponseLevelRetryHandler.Builder .withFixedDelay(Duration.ofSeconds(2), maxRetries) .retryForStatusCodes(com.yahoo.vespa.jdk8compat.List.of(HttpStatus.SC_SERVICE_UNAVAILABLE, HttpStatus.SC_BAD_GATEWAY)) .build(); HttpResponse response = createResponse(HttpStatus.SC_SERVICE_UNAVAILABLE); HttpClientContext ctx = new HttpClientContext(); int lastExecutionCount = maxRetries + 1; for (int i = 1; i < lastExecutionCount; i++) { assertTrue(handler.retryRequest(response, i, ctx)); } assertFalse(handler.retryRequest(response, lastExecutionCount, ctx)); }
@Test public void does_not_retry_for_non_listed_exception() { DelayedResponseLevelRetryHandler handler = DelayedResponseLevelRetryHandler.Builder .withFixedDelay(Duration.ofSeconds(2), 2) .retryForStatusCodes(com.yahoo.vespa.jdk8compat.List.of(HttpStatus.SC_SERVICE_UNAVAILABLE, HttpStatus.SC_BAD_GATEWAY)) .build(); HttpResponse response = createResponse(HttpStatus.SC_OK); HttpClientContext ctx = new HttpClientContext(); assertFalse(handler.retryRequest(response, 1, ctx)); }