@Override public RetryStrategy apply(final RetryContext retryOperationContext) { if (retryOperationContext.getAttempt() > maxAttempts) { return RetryStrategy.resume(retryOperationContext.getLatestError()); } else { final Object parameterObject = retryOperationContext.getLatestParameter(); return RetryStrategy.retryImmediately(parameterObject); } }
@Override public RetryStrategy apply(final RetryContext retryOperationContext) { return RetryStrategy.resume(retryOperationContext.getFirstError()); }
@Override public RetryStrategy apply(final RetryContext retryOperationContext) { return RetryStrategy.resume(retryOperationContext.getLatestError()); }
private List<RetryRule> createRules() { final Predicate<RetryContext> isFatal = r -> { final Throwable latestError = r.getLatestError(); final boolean unknownHost = latestError instanceof HttpException && latestError != null && latestError instanceof UnknownHostException; final boolean unauthorized = latestError instanceof UnauthorizedException; return unknownHost || unauthorized; }; final RetryRule fatalRetryRule = RetryRule.of(isFatal, RetryAction.ofShutdownServiceAndSendLatestException()); final RetryRule retryScheduledRetryRule = RetryRule.of(RetryPredicate.ofAlwaysTrue(), RetryAction.ofScheduledRetry(2, retryContext -> Duration.ofMillis(retryContext.getAttempt() * retryContext.getAttempt() * 50))); return asList(fatalRetryRule, retryScheduledRetryRule); }
@Test public void scheduledRetry() throws Exception { try (final Service service = new Failing2TimesServiceImpl()) { final RetryAction op = RetryAction.ofScheduledRetry(3, o -> Duration.ofMillis(o.getAttempt() * 100)); final List<RetryRule> retryRules = singletonList(RetryRule.of(RetryPredicate.ofAlwaysTrue(), op)); try(final AsyncRetrySupervisor supervisor = AsyncRetrySupervisor.of(retryRules)) { final CompletionStage<Integer> bar = supervisor.supervise(service, service::apply, "bar"); assertThat(waitAndGet(bar)).isEqualTo(3); assertThat(service.isClosed()).isFalse(); } } }
@Test public void immediateRetryRule() throws Exception { try (final Service service = new Failing2TimesServiceImpl()) { final List<RetryRule> retryRules = singletonList(RetryRule.of(RetryPredicate.ofAlwaysTrue(), RetryAction.ofImmediateRetries(3))); try(final AsyncRetrySupervisor supervisor = AsyncRetrySupervisor.of(retryRules)) { final CompletionStage<Integer> bar = supervisor.supervise(service, service::apply, "bar"); assertThat(waitAndGet(bar)).isEqualTo(3); assertThat(service.isClosed()).isFalse(); } } }
public static SphereClient ofRetry(final SphereClient delegate) { final int maxAttempts = 5; final List<RetryRule> retryRules = singletonList(RetryRule.of( RetryPredicate.ofMatchingStatusCodes(BAD_GATEWAY_502, SERVICE_UNAVAILABLE_503, GATEWAY_TIMEOUT_504), RetryAction.ofScheduledRetry(maxAttempts, context -> Duration.ofSeconds(context.getAttempt() * 2))) ); return RetrySphereClientDecorator.of(delegate, retryRules); } }
@Test public void giveUpAndSendFirstException() throws Exception { try (final Service service = new Failing2TimesServiceImpl()) { final List<RetryRule> retryRules = singletonList(RetryRule.of(RetryPredicate.ofAlwaysTrue(), RetryAction.ofGiveUpAndSendFirstException())); try(final AsyncRetrySupervisor supervisor = AsyncRetrySupervisor.of(retryRules)) { final CompletionStage<Integer> bar = supervisor.supervise(service, service::apply, "bar"); final Throwable throwable = catchThrowable(() -> waitAndGet(bar)); assertThat(throwable.getCause()).hasMessage(Failing2TimesServiceImpl.ERROR_MESSAGE); assertThat(service.isClosed()).isFalse(); } } }
@Override public RetryStrategy apply(final RetryContext retryOperationContext) { if (retryOperationContext.getAttempt() > maxAttempts) { return RetryStrategy.resume(retryOperationContext.getLatestError()); } else { final Duration duration = durationFunction.apply(retryOperationContext); final Object parameterObject = retryOperationContext.getLatestParameter(); return RetryStrategy.retryScheduled(parameterObject, duration); } }
private RetryStrategy applyContext(final RetryContext retryContext) { final Optional<RetryRule> matchingRetryRuleOption = findMatchingRetryRule(retryRules, retryContext); final RetryRule matchingRetryRule = matchingRetryRuleOption .orElseGet(() -> RetryRule.of(RetryPredicate.ofAlwaysTrue(), RetryAction.ofGiveUpAndSendLatestException())); return matchingRetryRule.apply(retryContext); }
@Override public RetryStrategy apply(final RetryContext retryOperationContext) { return RetryStrategy.stop(retryOperationContext.getLatestError()); }
@Override public RetryStrategy apply(final RetryContext retryOperationContext) { return RetryStrategy.stop(retryOperationContext.getFirstError()); }
@Override public <T> CompletionStage<T> execute(final SphereRequest<T> sphereRequest) { return supervisor.supervise(this, super::execute, sphereRequest); }
private <P, R> void retry(final RetryContextImpl<P, R> retryContext, final Function<P, CompletionStage<R>> function, final Object parameter) { final CompletionStage<R> completionStage = forceApply(function, parameter); handleResultAndEnqueueErrorHandlingAgain(completionStage, parameter, retryContext); }
RetrySphereClientImpl(final SphereClient delegate, final List<RetryRule> retryRules) { super(delegate); this.supervisor = AsyncRetrySupervisor.of(retryRules); }
/** * The error case should be dealt with by retrying later with the given parameter. * @param parameter parameter or parameter object to retry with, can be a new one or the old one * @param duration the amount of time to wait until the next try * @return strategy */ public static RetryStrategy retryScheduled(final Object parameter, final Duration duration) { return new RetryStrategy(StrategyType.RETRY_SCHEDULED, null, parameter, duration); }
public RetryContextImpl(final Instant startTimestamp, final Long attempt, final Throwable firstError, final Object firstParameter, final Throwable latestError, final Object latestParameter, final CompletableFuture<R> result, final Function<P, CompletionStage<R>> f, final AutoCloseable closeable, final BiConsumer<Runnable, Duration> scheduler) { this.attempt = attempt; this.startTimestamp = startTimestamp; this.firstError = filterOutCompletionException(firstError); this.latestError = filterOutCompletionException(latestError); this.firstParameter = firstParameter; this.latestParameter = latestParameter; this.result = result; this.f = f; this.closeable = closeable; this.scheduler = scheduler; }
private List<RetryRule> createRules() { final Predicate<RetryContext> isFatal = r -> { final Throwable latestError = r.getLatestError(); final boolean unknownHost = latestError instanceof HttpException && latestError != null && latestError instanceof UnknownHostException; final boolean unauthorized = latestError instanceof UnauthorizedException; return unknownHost || unauthorized; }; final RetryRule fatalRetryRule = RetryRule.of(isFatal, RetryAction.ofShutdownServiceAndSendLatestException()); final RetryRule retryScheduledRetryRule = RetryRule.of(RetryPredicate.ofAlwaysTrue(), RetryAction.ofScheduledRetry(2, retryContext -> Duration.ofMillis(retryContext.getAttempt() * retryContext.getAttempt() * 50))); return asList(fatalRetryRule, retryScheduledRetryRule); }
/** * The error case should be dealt with giving up and throwing an exception. * * @param error the exception to throw * @return strategy */ public static RetryStrategy resume(final Throwable error) { return new RetryStrategy(StrategyType.RESUME, error, null, null); }
/** * The error case should be dealt with by retrying immediately with the given parameter. * @param parameter parameter or parameter object to retry with, can be a new one or the old one * @return strategy */ public static RetryStrategy retryImmediately(final Object parameter) { return new RetryStrategy(StrategyType.RETRY_IMMEDIATELY, null, parameter, null); }