下面列出了org.springframework.boot.actuate.endpoint.annotation.ReadOperation#io.github.resilience4j.retry.Retry 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Test
public void shouldReplaceMetrics() {
Collection<FunctionCounter> counters = meterRegistry.get(DEFAULT_RETRY_CALLS)
.functionCounters();
Optional<FunctionCounter> successfulWithoutRetry = findMeterByKindAndNameTags(counters,
"successful_without_retry", retry.getName());
assertThat(successfulWithoutRetry).isPresent();
assertThat(successfulWithoutRetry.get().count())
.isEqualTo(retry.getMetrics().getNumberOfSuccessfulCallsWithoutRetryAttempt());
Retry newRetry = Retry.of(retry.getName(), RetryConfig.custom().maxAttempts(1).build());
retryRegistry.replace(retry.getName(), newRetry);
counters = meterRegistry.get(DEFAULT_RETRY_CALLS).functionCounters();
successfulWithoutRetry = findMeterByKindAndNameTags(counters, "successful_without_retry",
newRetry.getName());
assertThat(successfulWithoutRetry).isPresent();
assertThat(successfulWithoutRetry.get().count())
.isEqualTo(newRetry.getMetrics().getNumberOfSuccessfulCallsWithoutRetryAttempt());
}
/**
* This method returns a desired Tax(taxAmount, taxPercentage) value when the TaxService is up.
* If the TaxService is down, it applies a combination of following fault tolerance patterns
* in a sequence: TimeLimiter, CircuitBreaker and Retry using a Callable. When all the attempts
* are exhausted it calls a fallback method to recover from failure and offers the default tax value.
*
* @param amount
* @return
*/
public Tax applyResiliencePatterns(BigDecimal amount) {
CircuitBreaker circuitBreaker = configureCircuitBreaker();
TimeLimiter timeLimiter = configureTimeLimiter();
Retry retry = configureRetry();
Supplier<CompletableFuture<Tax>> futureSupplier = () -> CompletableFuture.supplyAsync(() -> salesOrderService.supplyTax(amount));
Callable<Tax> callable = TimeLimiter.decorateFutureSupplier(timeLimiter, futureSupplier);
callable = CircuitBreaker.decorateCallable(circuitBreaker, callable);
callable = Retry.decorateCallable(retry, callable);
//Executing the decorated callable and recovering from any exception by calling the fallback method
Try<Tax> result = Try.ofCallable(callable).recover(throwable -> taxServiceFallback(amount));
return result.get();
}
@Test
public void returnOnErrorUsingCompletable() throws InterruptedException {
RetryConfig config = retryConfig();
Retry retry = Retry.of("testName", config);
RetryTransformer<Object> retryTransformer = RetryTransformer.of(retry);
doThrow(new HelloWorldException()).when(helloWorldService).sayHelloWorld();
Completable.fromRunnable(helloWorldService::sayHelloWorld)
.compose(retryTransformer)
.test()
.await()
.assertError(HelloWorldException.class)
.assertNotComplete();
Completable.fromRunnable(helloWorldService::sayHelloWorld)
.compose(retryTransformer)
.test()
.await()
.assertError(HelloWorldException.class)
.assertNotComplete();
then(helloWorldService).should(times(6)).sayHelloWorld();
Retry.Metrics metrics = retry.getMetrics();
assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(2);
assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(0);
}
@Test
public void testDecorateSupplier() {
given(helloWorldService.returnHelloWorld()).willReturn("Hello world");
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
Supplier<String> decoratedSupplier = Decorators
.ofSupplier(() -> helloWorldService.returnHelloWorld())
.withCircuitBreaker(circuitBreaker)
.withRetry(Retry.ofDefaults("id"))
.withRateLimiter(RateLimiter.ofDefaults("testName"))
.withBulkhead(Bulkhead.ofDefaults("testName"))
.decorate();
String result = decoratedSupplier.get();
assertThat(result).isEqualTo("Hello world");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
then(helloWorldService).should(times(1)).returnHelloWorld();
}
@Test
public void doNotRetryFromPredicateUsingCompletable() {
RetryConfig config = RetryConfig.custom()
.retryOnException(t -> t instanceof IOException)
.waitDuration(Duration.ofMillis(50))
.maxAttempts(3).build();
Retry retry = Retry.of("testName", config);
doThrow(new HelloWorldException()).when(helloWorldService).sayHelloWorld();
Completable.fromRunnable(helloWorldService::sayHelloWorld)
.compose(RetryTransformer.of(retry))
.test()
.assertError(HelloWorldException.class)
.assertNotComplete()
.assertSubscribed();
then(helloWorldService).should().sayHelloWorld();
Retry.Metrics metrics = retry.getMetrics();
assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(1);
assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(0);
}
@Test
public void testDecoratorBuilderWithRetry() {
given(helloWorldService.returnHelloWorld()).willThrow(new RuntimeException("BAM!"));
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
Supplier<String> decoratedSupplier = Decorators
.ofSupplier(() -> helloWorldService.returnHelloWorld())
.withCircuitBreaker(circuitBreaker)
.withRetry(Retry.ofDefaults("id"))
.withBulkhead(Bulkhead.ofDefaults("testName"))
.decorate();
Try.of(decoratedSupplier::get);
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(3);
assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(3);
then(helloWorldService).should(times(3)).returnHelloWorld();
}
@Test
public void shouldTakeIntoAccountBackoffFunction() {
willThrow(new HelloWorldException()).given(helloWorldService).sayHelloWorld();
RetryConfig config = RetryConfig
.custom()
.intervalFunction(IntervalFunction.of(Duration.ofMillis(500), x -> x * x))
.build();
Retry retry = Retry.of("id", config);
CheckedRunnable retryableRunnable = Retry
.decorateCheckedRunnable(retry, helloWorldService::sayHelloWorld);
Try.run(retryableRunnable);
then(helloWorldService).should(times(3)).sayHelloWorld();
assertThat(sleptTime).isEqualTo(
RetryConfig.DEFAULT_WAIT_DURATION +
RetryConfig.DEFAULT_WAIT_DURATION * RetryConfig.DEFAULT_WAIT_DURATION);
}
@Test
public void shouldNotRetryWhenItThrowErrorSingle() {
RetryConfig config = retryConfig();
Retry retry = Retry.of("testName", config);
given(helloWorldService.returnHelloWorld())
.willThrow(new Error("BAM!"));
Single.fromCallable(helloWorldService::returnHelloWorld)
.compose(RetryTransformer.of(retry))
.test()
.assertError(Error.class)
.assertNotComplete()
.assertSubscribed();
then(helloWorldService).should().returnHelloWorld();
Retry.Metrics metrics = retry.getMetrics();
assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(0);
assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(0);
}
@Test
public void testDecorateCompletionStage() throws ExecutionException, InterruptedException {
given(helloWorldService.returnHelloWorld()).willReturn("Hello world");
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
Supplier<CompletionStage<String>> completionStageSupplier =
() -> CompletableFuture.supplyAsync(helloWorldService::returnHelloWorld);
CompletionStage<String> completionStage = Decorators
.ofCompletionStage(completionStageSupplier)
.withCircuitBreaker(circuitBreaker)
.withRetry(Retry.ofDefaults("id"), Executors.newSingleThreadScheduledExecutor())
.withBulkhead(Bulkhead.ofDefaults("testName"))
.get();
String value = completionStage.toCompletableFuture().get();
assertThat(value).isEqualTo("Hello world");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
then(helloWorldService).should(times(1)).returnHelloWorld();
}
@Test
public void doNotRetryFromPredicateUsingFlowable() {
RetryConfig config = RetryConfig.custom()
.retryOnException(t -> t instanceof IOException)
.waitDuration(Duration.ofMillis(50))
.maxAttempts(3).build();
Retry retry = Retry.of("testName", config);
given(helloWorldService.returnHelloWorld())
.willThrow(new HelloWorldException());
Flowable.fromCallable(helloWorldService::returnHelloWorld)
.compose(RetryTransformer.of(retry))
.test()
.assertError(HelloWorldException.class)
.assertNotComplete();
then(helloWorldService).should().returnHelloWorld();
Retry.Metrics metrics = retry.getMetrics();
assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(1);
assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(0);
}
@Test
public void shouldAddMetricsForANewlyCreatedRetry() {
Retry newRetry = retryRegistry.retry("backendB");
assertThat(taggedRetryMetricsPublisher.meterIdMap).containsKeys("backendA", "backendB");
assertThat(taggedRetryMetricsPublisher.meterIdMap.get("backendA")).hasSize(4);
assertThat(taggedRetryMetricsPublisher.meterIdMap.get("backendB")).hasSize(4);
List<Meter> meters = meterRegistry.getMeters();
assertThat(meters).hasSize(8);
Collection<FunctionCounter> counters = meterRegistry.get(DEFAULT_RETRY_CALLS)
.functionCounters();
Optional<FunctionCounter> successfulWithoutRetry = MetricsTestHelper
.findMeterByKindAndNameTags(counters, "successful_without_retry", newRetry.getName());
assertThat(successfulWithoutRetry).isPresent();
assertThat(successfulWithoutRetry.get().count())
.isEqualTo(newRetry.getMetrics().getNumberOfSuccessfulCallsWithoutRetryAttempt());
}
@Test
public void returnOnErrorUsingObservable() throws InterruptedException {
RetryConfig config = retryConfig();
Retry retry = Retry.of("testName", config);
RetryTransformer<Object> retryTransformer = RetryTransformer.of(retry);
given(helloWorldService.returnHelloWorld())
.willThrow(new HelloWorldException());
Observable.fromCallable(helloWorldService::returnHelloWorld)
.compose(retryTransformer)
.test()
.await()
.assertError(HelloWorldException.class)
.assertNotComplete();
Observable.fromCallable(helloWorldService::returnHelloWorld)
.compose(retryTransformer)
.test()
.await()
.assertError(HelloWorldException.class)
.assertNotComplete();
then(helloWorldService).should(times(6)).returnHelloWorld();
Retry.Metrics metrics = retry.getMetrics();
assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(2);
assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(0);
}
@Test
public void returnOnErrorUsingMaybe() throws InterruptedException {
RetryConfig config = retryConfig();
Retry retry = Retry.of("testName", config);
given(helloWorldService.returnHelloWorld())
.willThrow(new HelloWorldException());
Maybe.fromCallable(helloWorldService::returnHelloWorld)
.compose(RetryTransformer.of(retry))
.test()
.await()
.assertError(HelloWorldException.class)
.assertNotComplete();
Maybe.fromCallable(helloWorldService::returnHelloWorld)
.compose(RetryTransformer.of(retry))
.test()
.await()
.assertError(HelloWorldException.class)
.assertNotComplete();
then(helloWorldService).should(times(6)).returnHelloWorld();
Retry.Metrics metrics = retry.getMetrics();
assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(2);
assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(0);
}
@Test
public void shouldReplaceMetrics() {
Collection<FunctionCounter> counters = meterRegistry.get(DEFAULT_RETRY_CALLS).functionCounters();
Optional<FunctionCounter> successfulWithoutRetry = MetricsTestHelper.findMeterByKindAndNameTags(counters,
"successful_without_retry", retry.getName());
assertThat(successfulWithoutRetry).isPresent();
assertThat(successfulWithoutRetry.get().count())
.isEqualTo(retry.getMetrics().getNumberOfSuccessfulCallsWithoutRetryAttempt());
Retry newRetry = Retry.of(retry.getName(), RetryConfig.custom().maxAttempts(1).build());
retryRegistry.replace(retry.getName(), newRetry);
counters = meterRegistry.get(DEFAULT_RETRY_CALLS).functionCounters();
successfulWithoutRetry = MetricsTestHelper
.findMeterByKindAndNameTags(counters, "successful_without_retry",
newRetry.getName());
assertThat(successfulWithoutRetry).isPresent();
assertThat(successfulWithoutRetry.get().count())
.isEqualTo(newRetry.getMetrics().getNumberOfSuccessfulCallsWithoutRetryAttempt());
}
private <T> Supplier<T> decorateSupplierWithOnSuccess(Retry retry, Supplier<T> supplier) {
return () -> {
Retry.Context<T> context = retry.context();
do {
try {
T result = supplier.get();
final boolean validationOfResult = context.onResult(result);
if (!validationOfResult) {
context.onSuccess();
return result;
}
} catch (RuntimeException runtimeException) {
context.onRuntimeError(runtimeException);
}
} while (true);
};
}
@Test
public void retryOnResultUsingMono() {
RetryConfig config = RetryConfig.<String>custom()
.retryOnResult("retry"::equals)
.waitDuration(Duration.ofMillis(10))
.maxAttempts(3).build();
Retry retry = Retry.of("testName", config);
given(helloWorldService.returnHelloWorld())
.willReturn("retry")
.willReturn("success");
StepVerifier.create(Mono.fromCallable(helloWorldService::returnHelloWorld)
.transformDeferred(RetryOperator.of(retry)))
.expectSubscription()
.expectNext("success")
.expectComplete()
.verify(Duration.ofSeconds(1));
then(helloWorldService).should(times(2)).returnHelloWorld();
Retry.Metrics metrics = retry.getMetrics();
assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(0);
assertThat(metrics.getNumberOfSuccessfulCallsWithRetryAttempt()).isEqualTo(1);
}
@Test
public void retryOnResultUsingMaybe() throws InterruptedException {
RetryConfig config = RetryConfig.<String>custom()
.retryOnResult("retry"::equals)
.waitDuration(Duration.ofMillis(50))
.maxAttempts(3).build();
Retry retry = Retry.of("testName", config);
given(helloWorldService.returnHelloWorld())
.willReturn("retry")
.willReturn("success");
Maybe.fromCallable(helloWorldService::returnHelloWorld)
.compose(RetryTransformer.of(retry))
.test()
.await()
.assertValueCount(1)
.assertValue("success")
.assertComplete();
then(helloWorldService).should(times(2)).returnHelloWorld();
Retry.Metrics metrics = retry.getMetrics();
assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(0);
assertThat(metrics.getNumberOfSuccessfulCallsWithRetryAttempt()).isEqualTo(1);
}
@Test
public void retryOnResultFailAfterMaxAttemptsUsingMaybe() throws InterruptedException {
RetryConfig config = RetryConfig.<String>custom()
.retryOnResult("retry"::equals)
.waitDuration(Duration.ofMillis(50))
.maxAttempts(3).build();
Retry retry = Retry.of("testName", config);
given(helloWorldService.returnHelloWorld())
.willReturn("retry");
Maybe.fromCallable(helloWorldService::returnHelloWorld)
.compose(RetryTransformer.of(retry))
.test()
.await()
.assertValueCount(1)
.assertValue("retry")
.assertComplete()
.assertSubscribed();
then(helloWorldService).should(times(3)).returnHelloWorld();
}
@Test
public void testDecorateRunnable() {
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
Runnable decoratedRunnable = Decorators
.ofRunnable(() -> helloWorldService.sayHelloWorld())
.withCircuitBreaker(circuitBreaker)
.withRetry(Retry.ofDefaults("id"))
.withRateLimiter(RateLimiter.ofDefaults("testName"))
.withBulkhead(Bulkhead.ofDefaults("testName"))
.decorate();
decoratedRunnable.run();
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
then(helloWorldService).should(times(1)).sayHelloWorld();
}
@Test
public void retryOnResultFailAfterMaxAttemptsUsingObservable() throws InterruptedException {
RetryConfig config = RetryConfig.<String>custom()
.retryOnResult("retry"::equals)
.waitDuration(Duration.ofMillis(50))
.maxAttempts(3).build();
Retry retry = Retry.of("testName", config);
given(helloWorldService.returnHelloWorld())
.willReturn("retry");
Observable.fromCallable(helloWorldService::returnHelloWorld)
.compose(RetryTransformer.of(retry))
.test()
.await()
.assertValueCount(1)
.assertValue("retry")
.assertComplete();
then(helloWorldService).should(times(3)).returnHelloWorld();
}
@Test
public void doNotRetryFromPredicateUsingMaybe() {
RetryConfig config = RetryConfig.custom()
.retryOnException(t -> t instanceof IOException)
.waitDuration(Duration.ofMillis(50))
.maxAttempts(3).build();
Retry retry = Retry.of("testName", config);
given(helloWorldService.returnHelloWorld())
.willThrow(new HelloWorldException());
Maybe.fromCallable(helloWorldService::returnHelloWorld)
.compose(RetryTransformer.of(retry))
.test()
.assertError(HelloWorldException.class)
.assertNotComplete();
then(helloWorldService).should().returnHelloWorld();
Retry.Metrics metrics = retry.getMetrics();
assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(1);
assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(0);
}
@Test
public void shouldUseCustomPrefix() throws Throwable {
Retry retry = givenMetricRegistry("testPrefix", metricRegistry);
given(helloWorldService.returnHelloWorld()).willReturn("Hello world");
String value = retry.executeSupplier(helloWorldService::returnHelloWorld);
assertThat(value).isEqualTo("Hello world");
then(helloWorldService).should(times(1)).returnHelloWorld();
assertThat(metricRegistry.getMetrics()).hasSize(4);
assertThat(
metricRegistry.getGauges().get("testPrefix.testName." + SUCCESSFUL_CALLS_WITH_RETRY)
.getValue()).isEqualTo(0L);
assertThat(
metricRegistry.getGauges().get("testPrefix.testName." + SUCCESSFUL_CALLS_WITHOUT_RETRY)
.getValue()).isEqualTo(1L);
assertThat(metricRegistry.getGauges().get("testPrefix.testName." + FAILED_CALLS_WITH_RETRY)
.getValue()).isEqualTo(0L);
assertThat(
metricRegistry.getGauges().get("testPrefix.testName." + FAILED_CALLS_WITHOUT_RETRY)
.getValue()).isEqualTo(0L);
}
@Test
public void whenRetryIsUsed_thenItWorksAsExpected() {
RetryConfig config = RetryConfig.custom().maxAttempts(2).build();
RetryRegistry registry = RetryRegistry.of(config);
Retry retry = registry.retry("my");
Function<Integer, Void> decorated = Retry.decorateFunction(retry, (Integer s) -> {
service.process(s);
return null;
});
when(service.process(anyInt())).thenThrow(new RuntimeException());
try {
decorated.apply(1);
fail("Expected an exception to be thrown if all retries failed");
} catch (Exception e) {
verify(service, times(2)).process(any(Integer.class));
}
}
@Test
public void doNotRetryFromPredicateUsingSingle() {
RetryConfig config = RetryConfig.custom()
.retryOnException(t -> t instanceof IOException)
.waitDuration(Duration.ofMillis(50))
.maxAttempts(3).build();
Retry retry = Retry.of("testName", config);
given(helloWorldService.returnHelloWorld())
.willThrow(new HelloWorldException());
Single.fromCallable(helloWorldService::returnHelloWorld)
.compose(RetryTransformer.of(retry))
.test()
.assertError(HelloWorldException.class)
.assertNotComplete()
.assertSubscribed();
then(helloWorldService).should().returnHelloWorld();
Retry.Metrics metrics = retry.getMetrics();
assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(1);
assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(0);
}
@Test
public void testDecorateCheckedSupplier() throws IOException {
given(helloWorldService.returnHelloWorldWithException()).willReturn("Hello world");
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("helloBackend");
CheckedFunction0<String> decoratedSupplier = Decorators
.ofCheckedSupplier(() -> helloWorldService.returnHelloWorldWithException())
.withCircuitBreaker(circuitBreaker)
.withRetry(Retry.ofDefaults("id"))
.withRateLimiter(RateLimiter.ofDefaults("testName"))
.withBulkhead(Bulkhead.ofDefaults("testName"))
.decorate();
String result = Try.of(decoratedSupplier).get();
assertThat(result).isEqualTo("Hello world");
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1);
assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1);
then(helloWorldService).should(times(1)).returnHelloWorldWithException();
}
@Override
public List<MetricFamilySamples> collect() {
GaugeMetricFamily retryCallsFamily = new GaugeMetricFamily(
names.getCallsMetricName(),
"The number of calls",
LabelNames.NAME_AND_KIND
);
for (Retry retry : retryRegistry.getAllRetries()) {
retryCallsFamily.addMetric(asList(retry.getName(), "successful_without_retry"),
retry.getMetrics().getNumberOfSuccessfulCallsWithoutRetryAttempt());
retryCallsFamily.addMetric(asList(retry.getName(), "successful_with_retry"),
retry.getMetrics().getNumberOfSuccessfulCallsWithRetryAttempt());
retryCallsFamily.addMetric(asList(retry.getName(), "failed_without_retry"),
retry.getMetrics().getNumberOfFailedCallsWithoutRetryAttempt());
retryCallsFamily.addMetric(asList(retry.getName(), "failed_with_retry"),
retry.getMetrics().getNumberOfFailedCallsWithRetryAttempt());
}
return Collections.singletonList(retryCallsFamily);
}
private void shouldCompleteFutureAfterAttemptsInCaseOfExceptionAtSyncStage(int noOfAttempts) {
given(helloWorldService.returnHelloWorld())
.willThrow(new HelloWorldException());
Retry retryContext = Retry.of(
"id",
RetryConfig
.custom()
.maxAttempts(noOfAttempts)
.build());
Supplier<CompletionStage<String>> supplier = Retry.decorateCompletionStage(
retryContext,
scheduler,
() -> helloWorldService.returnHelloWorld());
Try<String> resultTry = Try.of(() -> awaitResult(supplier.get()));
then(helloWorldService).should(times(noOfAttempts)).returnHelloWorld();
assertThat(resultTry.isFailure()).isTrue();
assertThat(resultTry.getCause().getCause()).isInstanceOf(HelloWorldException.class);
}
private void shouldCompleteFutureAfterAttemptsInCaseOfExceptionAtAsyncStage(int noOfAttempts) {
CompletableFuture<String> failedFuture = new CompletableFuture<>();
failedFuture.completeExceptionally(new HelloWorldException());
given(helloWorldService.returnHelloWorld())
.willReturn(failedFuture);
Retry retryContext = Retry.of(
"id",
RetryConfig
.custom()
.maxAttempts(noOfAttempts)
.build());
Supplier<CompletionStage<String>> supplier = Retry.decorateCompletionStage(
retryContext,
scheduler,
() -> helloWorldService.returnHelloWorld());
Try<String> resultTry = Try.of(() -> awaitResult(supplier.get()));
then(helloWorldService).should(times(noOfAttempts)).returnHelloWorld();
assertThat(resultTry.isFailure()).isTrue();
assertThat(resultTry.getCause().getCause()).isInstanceOf(HelloWorldException.class);
}
private void shouldCompleteFutureAfterAttemptsInCaseOfRetyOnResultAtAsyncStage(int noOfAttempts,
String retryResponse) {
given(helloWorldService.returnHelloWorld())
.willReturn(completedFuture("Hello world"));
Retry retryContext = Retry.of(
"id",
RetryConfig
.<String>custom()
.maxAttempts(noOfAttempts)
.retryOnResult(s -> s.contains(retryResponse))
.build());
Supplier<CompletionStage<String>> supplier = Retry.decorateCompletionStage(
retryContext,
scheduler,
() -> helloWorldService.returnHelloWorld());
Try<String> resultTry = Try.of(() -> awaitResult(supplier.get()));
then(helloWorldService).should(times(noOfAttempts)).returnHelloWorld();
assertThat(resultTry.isSuccess()).isTrue();
}
@Test
public void shouldReturnAfterTwoAttempts() {
willThrow(new HelloWorldException()).willDoNothing().given(helloWorldService)
.sayHelloWorld();
Retry retry = Retry.ofDefaults("id");
TestSubscriber<RetryEvent.Type> testSubscriber = toFlowable(retry.getEventPublisher())
.map(RetryEvent::getEventType)
.test();
CheckedRunnable retryableRunnable = Retry
.decorateCheckedRunnable(retry, helloWorldService::sayHelloWorld);
Try<Void> result = Try.run(retryableRunnable);
then(helloWorldService).should(times(2)).sayHelloWorld();
assertThat(result.isSuccess()).isTrue();
assertThat(sleptTime).isEqualTo(RetryConfig.DEFAULT_WAIT_DURATION);
testSubscriber.assertValueCount(2)
.assertValues(RetryEvent.Type.RETRY, RetryEvent.Type.SUCCESS);
}