diff --git a/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java b/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java index f255d257b9..f37df75910 100644 --- a/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java +++ b/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java @@ -37,9 +37,15 @@ public class CircuitBreakerConfig { public static final int DEFAULT_MINIMUM_NUMBER_OF_CALLS = 100; public static final int DEFAULT_SLIDING_WINDOW_SIZE = 100; public static final int DEFAULT_SLOW_CALL_DURATION_THRESHOLD = 60; // Seconds - private static final Predicate DEFAULT_RECORD_FAILURE_PREDICATE = throwable -> true; + private static final Predicate DEFAULT_RECORD_EXCEPTION_PREDICATE = throwable -> true; + private static final Predicate DEFAULT_IGNORE_EXCEPTION_PREDICATE = throwable -> false; public static final SlidingWindow DEFAULT_SLIDING_WINDOW_TYPE = SlidingWindow.COUNT_BASED; + // The default exception predicate counts all exceptions as failures. + private Predicate recordExceptionPredicate = DEFAULT_RECORD_EXCEPTION_PREDICATE; + // The default exception predicate ignores no exceptions. + private Predicate ignoreExceptionPredicate = DEFAULT_IGNORE_EXCEPTION_PREDICATE; + @SuppressWarnings("unchecked") private Class[] recordExceptions = new Class[0]; @SuppressWarnings("unchecked") @@ -51,8 +57,6 @@ public class CircuitBreakerConfig { private SlidingWindow slidingWindowType = DEFAULT_SLIDING_WINDOW_TYPE; private int minimumNumberOfCalls = DEFAULT_MINIMUM_NUMBER_OF_CALLS; private Duration waitDurationInOpenState = Duration.ofSeconds(DEFAULT_WAIT_DURATION_IN_OPEN_STATE); - // The default exception predicate counts all exceptions as failures. - private Predicate recordFailurePredicate = DEFAULT_RECORD_FAILURE_PREDICATE; private boolean automaticTransitionFromOpenToHalfOpenEnabled = false; private float slowCallRateThreshold = DEFAULT_SLOW_CALL_RATE_THRESHOLD; private Duration slowCallDurationThreshold = Duration.ofSeconds(DEFAULT_SLOW_CALL_DURATION_THRESHOLD); @@ -100,8 +104,12 @@ public int getSlidingWindowSize() { return slidingWindowSize; } - public Predicate getRecordFailurePredicate() { - return recordFailurePredicate; + public Predicate getRecordExceptionPredicate() { + return recordExceptionPredicate; + } + + public Predicate getIgnoreExceptionPredicate() { + return ignoreExceptionPredicate; } public boolean isAutomaticTransitionFromOpenToHalfOpenEnabled() { @@ -128,14 +136,20 @@ public Duration getSlowCallDurationThreshold() { return slowCallDurationThreshold; } + + public static class Builder { @Nullable - private Predicate recordFailurePredicate; + private Predicate recordExceptionPredicate; + @Nullable + private Predicate ignoreExceptionPredicate; + @SuppressWarnings("unchecked") private Class[] recordExceptions = new Class[0]; @SuppressWarnings("unchecked") private Class[] ignoreExceptions = new Class[0]; + private float failureRateThreshold = DEFAULT_FAILURE_RATE_THRESHOLD; private int minimumNumberOfCalls = DEFAULT_MINIMUM_NUMBER_OF_CALLS; private int permittedNumberOfCallsInHalfOpenState = DEFAULT_PERMITTED_CALLS_IN_HALF_OPEN_STATE; @@ -155,7 +169,8 @@ public Builder(CircuitBreakerConfig baseConfig) { this.failureRateThreshold = baseConfig.failureRateThreshold; this.ignoreExceptions = baseConfig.ignoreExceptions; this.recordExceptions = baseConfig.recordExceptions; - this.recordFailurePredicate = baseConfig.recordFailurePredicate; + this.recordExceptionPredicate = baseConfig.recordExceptionPredicate; + this.ignoreExceptionPredicate = baseConfig.ignoreExceptionPredicate; this.automaticTransitionFromOpenToHalfOpenEnabled = baseConfig.automaticTransitionFromOpenToHalfOpenEnabled; this.slowCallRateThreshold = baseConfig.slowCallRateThreshold; this.slowCallDurationThreshold = baseConfig.slowCallDurationThreshold; @@ -367,16 +382,38 @@ public Builder slidingWindowType(SlidingWindow slidingWindowType) { return this; } + /** + * @deprecated use {@link #recordException(Predicate)} instead. + */ + @Deprecated + public Builder recordFailure(Predicate predicate) { + this.recordExceptionPredicate = predicate; + return this; + } + /** * Configures a Predicate which evaluates if an exception should be recorded as a failure and thus increase the failure rate. * The Predicate must return true if the exception should count as a failure. The Predicate must return false, if the exception - * should neither count as a failure nor success. + * should count as a success, unless the exception is explicitly ignored by {@link #ignoreExceptions(Class[])} or {@link #ignoreException(Predicate)}. * * @param predicate the Predicate which evaluates if an exception should count as a failure * @return the CircuitBreakerConfig.Builder */ - public Builder recordFailure(Predicate predicate) { - this.recordFailurePredicate = predicate; + public Builder recordException(Predicate predicate) { + this.recordExceptionPredicate = predicate; + return this; + } + + /** + * Configures a Predicate which evaluates if an exception should be ignored and neither count as a failure nor success. + * The Predicate must return true if the exception should be ignored . + * The Predicate must return false, if the exception should count as a failure. + * + * @param predicate the Predicate which evaluates if an exception should count as a failure + * @return the CircuitBreakerConfig.Builder + */ + public Builder ignoreException(Predicate predicate) { + this.ignoreExceptionPredicate = predicate; return this; } @@ -390,10 +427,10 @@ public Builder recordFailure(Predicate predicate) { *

* Example: * recordExceptions(Throwable.class) and ignoreExceptions(RuntimeException.class) - * would capture all Errors and checked Exceptions, and ignore unchecked + * would capture all Errors and checked Exceptions, and ignore RuntimeExceptions. *

* For a more sophisticated exception management use the - * @see #recordFailure(Predicate) method + * @see #recordException(Predicate) method */ @SuppressWarnings("unchecked") @SafeVarargs @@ -412,14 +449,14 @@ public final Builder recordExceptions(@Nullable Class... er *

* Example: * ignoreExceptions(Throwable.class) and recordExceptions(Exception.class) - * would capture nothing + * would capture nothing. *

* Example: * ignoreExceptions(Exception.class) and recordExceptions(Throwable.class) - * would capture Errors + * would capture Errors. *

* For a more sophisticated exception management use the - * @see #recordFailure(Predicate) method + * @see #ignoreException(Predicate) method */ @SuppressWarnings("unchecked") @SafeVarargs @@ -466,23 +503,22 @@ public CircuitBreakerConfig build() { config.recordExceptions = recordExceptions; config.ignoreExceptions = ignoreExceptions; config.automaticTransitionFromOpenToHalfOpenEnabled = automaticTransitionFromOpenToHalfOpenEnabled; - config.recordFailurePredicate = createRecordFailurePredicate(); + config.recordExceptionPredicate = createRecordExceptionPredicate(); + config.ignoreExceptionPredicate = createIgnoreFailurePredicate(); return config; } - private Predicate createRecordFailurePredicate() { - return createRecordExceptionPredicate() - .and(PredicateCreator.createIgnoreExceptionsPredicate(ignoreExceptions) - .orElse(DEFAULT_RECORD_FAILURE_PREDICATE)); + private Predicate createIgnoreFailurePredicate() { + return PredicateCreator.createExceptionsPredicate(ignoreExceptions) + .map(predicate -> ignoreExceptionPredicate != null ? predicate.or(ignoreExceptionPredicate) : predicate) + .orElseGet(() -> ignoreExceptionPredicate != null ? ignoreExceptionPredicate : DEFAULT_IGNORE_EXCEPTION_PREDICATE); } private Predicate createRecordExceptionPredicate() { - return PredicateCreator.createRecordExceptionsPredicate(recordExceptions) - .map(predicate -> recordFailurePredicate != null ? predicate.or(recordFailurePredicate) : predicate) - .orElseGet(() -> recordFailurePredicate != null ? recordFailurePredicate : DEFAULT_RECORD_FAILURE_PREDICATE); + return PredicateCreator.createExceptionsPredicate(recordExceptions) + .map(predicate -> recordExceptionPredicate != null ? predicate.or(recordExceptionPredicate) : predicate) + .orElseGet(() -> recordExceptionPredicate != null ? recordExceptionPredicate : DEFAULT_RECORD_EXCEPTION_PREDICATE); } - - } public enum SlidingWindow { diff --git a/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachine.java b/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachine.java index d2d3c5f3ae..410302245b 100644 --- a/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachine.java +++ b/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachine.java @@ -37,7 +37,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Predicate; import java.util.function.Supplier; import java.util.function.UnaryOperator; @@ -155,24 +154,29 @@ public void acquirePermission() { public void onError(long duration, TimeUnit durationUnit, Throwable throwable) { // Handle the case if the completable future throw CompletionException wrapping the original exception // where original exception is the the one to retry not the CompletionException. - Predicate recordFailurePredicate = circuitBreakerConfig.getRecordFailurePredicate(); if (throwable instanceof CompletionException) { Throwable cause = throwable.getCause(); - handleThrowable(duration, durationUnit, recordFailurePredicate, cause); + handleThrowable(duration, durationUnit, cause); }else{ - handleThrowable(duration, durationUnit, recordFailurePredicate, throwable); + handleThrowable(duration, durationUnit, throwable); } } - private void handleThrowable(long duration, TimeUnit durationUnit, Predicate recordFailurePredicate, Throwable throwable) { - if (recordFailurePredicate.test(throwable)) { - LOG.debug("CircuitBreaker '{}' recorded a failure:", name, throwable); - publishCircuitErrorEvent(name, duration, durationUnit, throwable); - stateReference.get().onError(duration, durationUnit, throwable); - } else { + private void handleThrowable(long duration, TimeUnit durationUnit, Throwable throwable) { + if(circuitBreakerConfig.getIgnoreExceptionPredicate().test(throwable)){ + LOG.debug("CircuitBreaker '{}' ignored an exception:", name, throwable); releasePermission(); publishCircuitIgnoredErrorEvent(name, duration,durationUnit, throwable); } + else if(circuitBreakerConfig.getRecordExceptionPredicate().test(throwable)){ + LOG.debug("CircuitBreaker '{}' recorded an exception as failure:", name, throwable); + publishCircuitErrorEvent(name, duration, durationUnit, throwable); + stateReference.get().onError(duration, durationUnit, throwable); + }else{ + LOG.debug("CircuitBreaker '{}' recorded an exception as success:", name, throwable); + publishSuccessEvent(duration, durationUnit); + stateReference.get().onSuccess(duration, durationUnit); + } } @Override diff --git a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfigTest.java b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfigTest.java index 760d30f5e3..b6d9377e40 100644 --- a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfigTest.java +++ b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfigTest.java @@ -29,8 +29,6 @@ public class CircuitBreakerConfigTest { - private static final Predicate TEST_PREDICATE = e -> "test".equals(e.getMessage()); - @Test(expected = IllegalArgumentException.class) public void zeroMaxFailuresShouldFail() { custom().failureRateThreshold(0).build(); @@ -110,7 +108,7 @@ public void shouldSetDefaultSettings() { then(circuitBreakerConfig.getSlidingWindowType()).isEqualTo(DEFAULT_SLIDING_WINDOW_TYPE); then(circuitBreakerConfig.getMinimumNumberOfCalls()).isEqualTo(DEFAULT_MINIMUM_NUMBER_OF_CALLS); then(circuitBreakerConfig.getWaitDurationInOpenState().getSeconds()).isEqualTo(DEFAULT_SLOW_CALL_DURATION_THRESHOLD); - then(circuitBreakerConfig.getRecordFailurePredicate()).isNotNull(); + then(circuitBreakerConfig.getRecordExceptionPredicate()).isNotNull(); then(circuitBreakerConfig.getSlowCallRateThreshold()).isEqualTo(DEFAULT_SLOW_CALL_RATE_THRESHOLD); then(circuitBreakerConfig.getSlowCallDurationThreshold().getSeconds()).isEqualTo(DEFAULT_SLOW_CALL_DURATION_THRESHOLD); } @@ -206,14 +204,27 @@ public void shouldSetWaitInterval() { } @Test - public void shouldUseRecordFailureThrowablePredicate() { + public void shouldUseCustomRecordExceptionPredicate() { + CircuitBreakerConfig circuitBreakerConfig = custom() + .recordException(e -> "test".equals(e.getMessage())).build(); + Predicate recordExceptionPredicate = circuitBreakerConfig.getRecordExceptionPredicate(); + then(recordExceptionPredicate.test(new Error("test"))).isEqualTo(true); + then(recordExceptionPredicate.test(new Error("fail"))).isEqualTo(false); + then(recordExceptionPredicate.test(new RuntimeException("test"))).isEqualTo(true); + then(recordExceptionPredicate.test(new Error())).isEqualTo(false); + then(recordExceptionPredicate.test(new RuntimeException())).isEqualTo(false); + } + + @Test + public void shouldUseCustomIgnoreExceptionPredicate() { CircuitBreakerConfig circuitBreakerConfig = custom() - .recordFailure(TEST_PREDICATE).build(); - then(circuitBreakerConfig.getRecordFailurePredicate().test(new Error("test"))).isEqualTo(true); - then(circuitBreakerConfig.getRecordFailurePredicate().test(new Error("fail"))).isEqualTo(false); - then(circuitBreakerConfig.getRecordFailurePredicate().test(new RuntimeException("test"))).isEqualTo(true); - then(circuitBreakerConfig.getRecordFailurePredicate().test(new Error())).isEqualTo(false); - then(circuitBreakerConfig.getRecordFailurePredicate().test(new RuntimeException())).isEqualTo(false); + .ignoreException(e -> "ignore".equals(e.getMessage())).build(); + Predicate ignoreExceptionPredicate = circuitBreakerConfig.getIgnoreExceptionPredicate(); + then(ignoreExceptionPredicate.test(new Error("ignore"))).isEqualTo(true); + then(ignoreExceptionPredicate.test(new Error("fail"))).isEqualTo(false); + then(ignoreExceptionPredicate.test(new RuntimeException("ignore"))).isEqualTo(true); + then(ignoreExceptionPredicate.test(new Error())).isEqualTo(false); + then(ignoreExceptionPredicate.test(new RuntimeException())).isEqualTo(false); } private static class ExtendsException extends Exception { @@ -222,70 +233,71 @@ private static class ExtendsException extends Exception { } private static class ExtendsRuntimeException extends RuntimeException {} private static class ExtendsExtendsException extends ExtendsException {} - private static class ExtendsException2 extends Exception {} + private static class BusinessException extends Exception {} private static class ExtendsError extends Error {} @Test - public void shouldUseIgnoreExceptionToBuildPredicate() { + public void shouldUseIgnoreExceptionsToBuildPredicate() { CircuitBreakerConfig circuitBreakerConfig = custom() - .ignoreExceptions(RuntimeException.class, ExtendsExtendsException.class).build(); - final Predicate failurePredicate = circuitBreakerConfig.getRecordFailurePredicate(); - then(failurePredicate.test(new Exception())).isEqualTo(true); // not explicitly excluded - then(failurePredicate.test(new ExtendsError())).isEqualTo(true); // not explicitly excluded - then(failurePredicate.test(new ExtendsException())).isEqualTo(true); // not explicitly excluded - then(failurePredicate.test(new ExtendsException2())).isEqualTo(true); // not explicitly excluded - then(failurePredicate.test(new RuntimeException())).isEqualTo(false); // explicitly excluded - then(failurePredicate.test(new ExtendsRuntimeException())).isEqualTo(false); // inherits excluded from ExtendsException - then(failurePredicate.test(new ExtendsExtendsException())).isEqualTo(false); // explicitly excluded + .ignoreExceptions(RuntimeException.class, ExtendsExtendsException.class, BusinessException.class).build(); + final Predicate ignoreExceptionPredicate = circuitBreakerConfig.getIgnoreExceptionPredicate(); + then(ignoreExceptionPredicate.test(new Exception())).isEqualTo(false); // not explicitly ignored + then(ignoreExceptionPredicate.test(new ExtendsError())).isEqualTo(false); // not explicitly ignored + then(ignoreExceptionPredicate.test(new ExtendsException())).isEqualTo(false); // not explicitly ignored + then(ignoreExceptionPredicate.test(new BusinessException())).isEqualTo(true); // explicitly ignored + then(ignoreExceptionPredicate.test(new RuntimeException())).isEqualTo(true); // explicitly ignored + then(ignoreExceptionPredicate.test(new ExtendsRuntimeException())).isEqualTo(true); // inherits ignored because of RuntimeException is ignored + then(ignoreExceptionPredicate.test(new ExtendsExtendsException())).isEqualTo(true); // explicitly ignored } @Test - public void shouldUseRecordExceptionToBuildPredicate() { + public void shouldUseRecordExceptionsToBuildPredicate() { CircuitBreakerConfig circuitBreakerConfig = custom() .recordExceptions(RuntimeException.class, ExtendsExtendsException.class).build(); - final Predicate failurePredicate = circuitBreakerConfig.getRecordFailurePredicate(); - then(failurePredicate.test(new Exception())).isEqualTo(false); // not explicitly included + final Predicate failurePredicate = circuitBreakerConfig.getRecordExceptionPredicate(); + then(failurePredicate.test(new Exception())).isEqualTo(false); // not explicitly recore then(failurePredicate.test(new ExtendsError())).isEqualTo(false); // not explicitly included then(failurePredicate.test(new ExtendsException())).isEqualTo(false); // not explicitly included - then(failurePredicate.test(new ExtendsException2())).isEqualTo(false); // not explicitly included + then(failurePredicate.test(new BusinessException())).isEqualTo(false); // not explicitly included then(failurePredicate.test(new RuntimeException())).isEqualTo(true); // explicitly included - then(failurePredicate.test(new ExtendsRuntimeException())).isEqualTo(true); // inherits included from ExtendsException + then(failurePredicate.test(new ExtendsRuntimeException())).isEqualTo(true); // inherits included because RuntimeException is included then(failurePredicate.test(new ExtendsExtendsException())).isEqualTo(true); // explicitly included } @Test - public void shouldUseIgnoreExceptionOverRecordToBuildPredicate() { + public void shouldCreateCombinedRecordExceptionPredicate() { CircuitBreakerConfig circuitBreakerConfig = custom() - .recordExceptions(RuntimeException.class, ExtendsExtendsException.class) - .ignoreExceptions(ExtendsException.class, ExtendsRuntimeException.class) + .recordException(e -> "test".equals(e.getMessage())) //1 + .recordExceptions(RuntimeException.class, ExtendsExtendsException.class) //2 .build(); - final Predicate failurePredicate = circuitBreakerConfig.getRecordFailurePredicate(); - then(failurePredicate.test(new Exception())).isEqualTo(false); // not explicitly included - then(failurePredicate.test(new ExtendsError())).isEqualTo(false); // not explicitly included - then(failurePredicate.test(new ExtendsException())).isEqualTo(false); // explicitly excluded - then(failurePredicate.test(new ExtendsException2())).isEqualTo(false); // not explicitly included - then(failurePredicate.test(new RuntimeException())).isEqualTo(true); // explicitly included - then(failurePredicate.test(new ExtendsRuntimeException())).isEqualTo(false); // explicitly excluded - then(failurePredicate.test(new ExtendsExtendsException())).isEqualTo(false); // inherits excluded from ExtendsException + final Predicate recordExceptionPredicate = circuitBreakerConfig.getRecordExceptionPredicate(); + then(recordExceptionPredicate.test(new Exception())).isEqualTo(false); // not explicitly included + then(recordExceptionPredicate.test(new Exception("test"))).isEqualTo(true); // explicitly included by 1 + then(recordExceptionPredicate.test(new ExtendsError())).isEqualTo(false); // not explicitly included + then(recordExceptionPredicate.test(new ExtendsException())).isEqualTo(false); // explicitly excluded by 3 + then(recordExceptionPredicate.test(new ExtendsException("test"))).isEqualTo(true); // explicitly included by 1 + then(recordExceptionPredicate.test(new BusinessException())).isEqualTo(false); // not explicitly included + then(recordExceptionPredicate.test(new RuntimeException())).isEqualTo(true); // explicitly included by 2 + then(recordExceptionPredicate.test(new ExtendsRuntimeException())).isEqualTo(true); // implicitly included by RuntimeException + then(recordExceptionPredicate.test(new ExtendsExtendsException())).isEqualTo(true); // explicitly included } @Test - public void shouldUseBothRecordToBuildPredicate() { + public void shouldCreateCombinedIgnoreExceptionPredicate() { CircuitBreakerConfig circuitBreakerConfig = custom() - .recordFailure(TEST_PREDICATE) //1 - .recordExceptions(RuntimeException.class, ExtendsExtendsException.class) //2 - .ignoreExceptions(ExtendsException.class, ExtendsRuntimeException.class) //3 + .ignoreException(e -> "ignore".equals(e.getMessage())) //1 + .ignoreExceptions(BusinessException.class, ExtendsExtendsException.class, ExtendsRuntimeException.class) //2 .build(); - final Predicate failurePredicate = circuitBreakerConfig.getRecordFailurePredicate(); - then(failurePredicate.test(new Exception())).isEqualTo(false); // not explicitly included - then(failurePredicate.test(new Exception("test"))).isEqualTo(true); // explicitly included by 1 - then(failurePredicate.test(new ExtendsError())).isEqualTo(false); // ot explicitly included - then(failurePredicate.test(new ExtendsException())).isEqualTo(false); // explicitly excluded by 3 - then(failurePredicate.test(new ExtendsException("test"))).isEqualTo(false); // explicitly excluded by 3 even if included by 1 - then(failurePredicate.test(new ExtendsException2())).isEqualTo(false); // not explicitly included - then(failurePredicate.test(new RuntimeException())).isEqualTo(true); // explicitly included by 2 - then(failurePredicate.test(new ExtendsRuntimeException())).isEqualTo(false); // explicitly excluded by 3 - then(failurePredicate.test(new ExtendsExtendsException())).isEqualTo(false); // inherits excluded from ExtendsException by 3 + final Predicate ignoreExceptionPredicate = circuitBreakerConfig.getIgnoreExceptionPredicate(); + then(ignoreExceptionPredicate.test(new Exception())).isEqualTo(false); // not explicitly ignored + then(ignoreExceptionPredicate.test(new Exception("ignore"))).isEqualTo(true); // explicitly ignored by 1 + then(ignoreExceptionPredicate.test(new ExtendsError())).isEqualTo(false); // not explicitly ignored + then(ignoreExceptionPredicate.test(new ExtendsException())).isEqualTo(false); // not explicitly ignored + then(ignoreExceptionPredicate.test(new ExtendsException("ignore"))).isEqualTo(true); // explicitly ignored 1 + then(ignoreExceptionPredicate.test(new BusinessException())).isEqualTo(true); // explicitly ignored 2 + then(ignoreExceptionPredicate.test(new RuntimeException())).isEqualTo(false); // not explicitly ignored + then(ignoreExceptionPredicate.test(new ExtendsRuntimeException())).isEqualTo(true); // explicitly ignored 2 + then(ignoreExceptionPredicate.test(new ExtendsExtendsException())).isEqualTo(true); // implicitly ignored by ExtendsRuntimeException } @Test diff --git a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerEventPublisherTest.java b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerEventPublisherTest.java index c4c97986bb..3d34d77a26 100644 --- a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerEventPublisherTest.java +++ b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerEventPublisherTest.java @@ -23,12 +23,9 @@ import org.junit.Test; import org.slf4j.Logger; -import javax.xml.ws.WebServiceException; import java.io.IOException; import java.util.concurrent.TimeUnit; -import static io.vavr.API.*; -import static io.vavr.Predicates.instanceOf; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.mock; @@ -96,7 +93,7 @@ public void shouldConsumeOnResetEvent() { @Test public void shouldConsumeOnStateTransitionEvent() { circuitBreaker = CircuitBreaker.of("test", CircuitBreakerConfig.custom() - .ringBufferSizeInClosedState(1).build()); + .slidingWindowSize(1).build()); circuitBreaker.getEventPublisher() .onStateTransition(this::logEventType); @@ -110,7 +107,7 @@ public void shouldConsumeOnStateTransitionEvent() { @Test public void shouldConsumeCallNotPermittedEvent() { circuitBreaker = CircuitBreaker.of("test", CircuitBreakerConfig.custom() - .ringBufferSizeInClosedState(1).build()); + .slidingWindowSize(1).build()); circuitBreaker.getEventPublisher() .onCallNotPermitted(this::logEventType); @@ -126,7 +123,7 @@ public void shouldConsumeCallNotPermittedEvent() { public void shouldNotProduceEventsInDisabledState() { //Given circuitBreaker = CircuitBreaker.of("test", CircuitBreakerConfig.custom() - .ringBufferSizeInClosedState(1).build()); + .slidingWindowSize(1).build()); circuitBreaker.getEventPublisher() .onEvent(this::logEventType); @@ -155,9 +152,7 @@ private void logEventType(CircuitBreakerEvent event) { @Test public void shouldConsumeIgnoredErrorEvent() { CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() - .recordFailure(throwable -> Match(throwable).of( - Case($(instanceOf(WebServiceException.class)), true), - Case($(), false))) + .ignoreExceptions(IOException.class) .build(); circuitBreaker = CircuitBreaker.of("test", circuitBreakerConfig); diff --git a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerTest.java b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerTest.java index bb9806460d..9b128ae013 100644 --- a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerTest.java +++ b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerTest.java @@ -39,8 +39,6 @@ import java.util.function.Function; import java.util.function.Supplier; -import static io.vavr.API.*; -import static io.vavr.Predicates.instanceOf; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -515,8 +513,8 @@ public void shouldReturnFailureWithCircuitBreakerOpenException() { // Given // Create a custom configuration for a CircuitBreaker CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() - .ringBufferSizeInClosedState(2) - .ringBufferSizeInHalfOpenState(2) + .slidingWindowSize(2) + .permittedNumberOfCallsInHalfOpenState(2) .failureRateThreshold(50) .waitDurationInOpenState(Duration.ofMillis(1000)) .build(); @@ -569,12 +567,10 @@ public void shouldNotRecordIOExceptionAsAFailure() { // tag::shouldNotRecordIOExceptionAsAFailure[] // Given CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() - .ringBufferSizeInClosedState(2) - .ringBufferSizeInHalfOpenState(2) + .slidingWindowSize(2) + .permittedNumberOfCallsInHalfOpenState(2) .waitDurationInOpenState(Duration.ofMillis(1000)) - .recordFailure(throwable -> Match(throwable).of( - Case($(instanceOf(WebServiceException.class)), true), - Case($(), false))) + .ignoreExceptions(IOException.class) .build(); CircuitBreaker circuitBreaker = CircuitBreaker.of("testName", circuitBreakerConfig); @@ -646,7 +642,7 @@ public void shouldThrowCircuitBreakerOpenException() { // tag::shouldThrowCircuitBreakerOpenException[] // Given CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() - .ringBufferSizeInClosedState(2) + .slidingWindowSize(2) .waitDurationInOpenState(Duration.ofMillis(1000)) .build(); CircuitBreaker circuitBreaker = CircuitBreaker.of("testName", circuitBreakerConfig); diff --git a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerExceptionHandlingTest.java b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerExceptionHandlingTest.java new file mode 100644 index 0000000000..0840ba7920 --- /dev/null +++ b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerExceptionHandlingTest.java @@ -0,0 +1,99 @@ +package io.github.resilience4j.circuitbreaker.internal; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import org.junit.Test; + +import java.io.IOException; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import static io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.custom; +import static org.assertj.core.api.Assertions.assertThat; + +public class CircuitBreakerExceptionHandlingTest { + + private static class BusinessException extends Exception { + + public BusinessException(String message) { + super(message); + } + } + + @Test + public void shouldRecordRuntimeExceptionAsFailureAndBusinessExceptionAsSuccess() { + CircuitBreaker circuitBreaker = new CircuitBreakerStateMachine("testName", custom() + .slidingWindowSize(5) + .recordException(ex -> !(ex instanceof BusinessException)) + .build()); + + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new RuntimeException()); + + // Call 2 is a failure + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new BusinessException("test")); + + assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls()).isEqualTo(1); + assertThat(circuitBreaker.getMetrics().getNumberOfSuccessfulCalls()).isEqualTo(1); + assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls()).isEqualTo(2); + } + + @Test + public void shouldRecordIOExceptionAsFailureAndBusinessExceptionAsSuccess() { + CircuitBreaker circuitBreaker = new CircuitBreakerStateMachine("testName", custom() + .slidingWindowSize(5) + .recordExceptions(IOException.class) + .build()); + + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new IOException()); + + // Call 2 is a failure + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new BusinessException("test")); + + assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls()).isEqualTo(1); + assertThat(circuitBreaker.getMetrics().getNumberOfSuccessfulCalls()).isEqualTo(1); + assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls()).isEqualTo(2); + } + + @Test + public void shouldRecordBusinessExceptionAsFailure() { + CircuitBreaker circuitBreaker = new CircuitBreakerStateMachine("testName", custom() + .slidingWindowSize(5) + .recordException(ex -> "record".equals(ex.getMessage())) + .build()); + + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new BusinessException("record")); + + // Call 2 is a failure + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new BusinessException("bla")); + + assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls()).isEqualTo(1); + assertThat(circuitBreaker.getMetrics().getNumberOfSuccessfulCalls()).isEqualTo(1); + assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls()).isEqualTo(2); + } + + @Test + public void shouldIgnoreNumberFormatException() { + CircuitBreaker circuitBreaker = new CircuitBreakerStateMachine("testName", custom() + .failureRateThreshold(50) + .slidingWindowSize(5) + .waitDurationInOpenState(Duration.ofSeconds(5)) + .ignoreExceptions(NumberFormatException.class) + .build()); + + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new RuntimeException()); + + // Call 2 is a failure + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onError(0, TimeUnit.NANOSECONDS, new NumberFormatException()); + + assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls()).isEqualTo(1); + assertThat(circuitBreaker.getMetrics().getNumberOfSuccessfulCalls()).isEqualTo(0); + assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls()).isEqualTo(1); + } +} diff --git a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachineTest.java b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachineTest.java index 46a6ff8c86..6cd66fa268 100644 --- a/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachineTest.java +++ b/resilience4j-circuitbreaker/src/test/java/io/github/resilience4j/circuitbreaker/internal/CircuitBreakerStateMachineTest.java @@ -49,7 +49,7 @@ public void setUp() { .slowCallRateThreshold(50) .slidingWindow(5, 5, SlidingWindow.COUNT_BASED) .waitDurationInOpenState(Duration.ofSeconds(5)) - .recordFailure(error -> !(error instanceof NumberFormatException)) + .ignoreExceptions(NumberFormatException.class) .build(), mockClock); } diff --git a/resilience4j-consumer/src/test/java/io/github/resilience4j/consumer/CircularEventConsumerTest.java b/resilience4j-consumer/src/test/java/io/github/resilience4j/consumer/CircularEventConsumerTest.java index 0c19237579..d1f6b33132 100644 --- a/resilience4j-consumer/src/test/java/io/github/resilience4j/consumer/CircularEventConsumerTest.java +++ b/resilience4j-consumer/src/test/java/io/github/resilience4j/consumer/CircularEventConsumerTest.java @@ -21,7 +21,6 @@ import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.event.CircuitBreakerEvent; -import io.vavr.API; import org.junit.Test; import javax.xml.ws.WebServiceException; @@ -29,9 +28,6 @@ import java.util.concurrent.TimeUnit; import static io.github.resilience4j.circuitbreaker.event.CircuitBreakerEvent.Type; -import static io.vavr.API.$; -import static io.vavr.API.Case; -import static io.vavr.Predicates.instanceOf; import static org.assertj.core.api.Assertions.assertThat; public class CircularEventConsumerTest { @@ -68,10 +64,8 @@ public void shouldBufferErrorEvents() { public void shouldBufferAllEvents() { // Given CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() - .ringBufferSizeInClosedState(3) - .recordFailure(throwable -> API.Match(throwable).of( - Case($(instanceOf(WebServiceException.class)), true), - Case($(), false))) + .slidingWindowSize(3) + .ignoreExceptions(IOException.class) .build(); CircuitBreaker circuitBreaker = CircuitBreaker.of("testName", circuitBreakerConfig); CircularEventConsumer ringBuffer = new CircularEventConsumer<>(10); diff --git a/resilience4j-core/src/main/java/io/github/resilience4j/core/predicate/PredicateCreator.java b/resilience4j-core/src/main/java/io/github/resilience4j/core/predicate/PredicateCreator.java index c585086048..19a2ad788e 100644 --- a/resilience4j-core/src/main/java/io/github/resilience4j/core/predicate/PredicateCreator.java +++ b/resilience4j-core/src/main/java/io/github/resilience4j/core/predicate/PredicateCreator.java @@ -9,12 +9,12 @@ public class PredicateCreator { private PredicateCreator() {} @SafeVarargs - public static Optional> createRecordExceptionsPredicate(Class ...recordExceptions) { + public static Optional> createExceptionsPredicate(Class ...recordExceptions) { return exceptionPredicate(recordExceptions); } @SafeVarargs - public static Optional> createIgnoreExceptionsPredicate(Class ...ignoreExceptions) { + public static Optional> createNegatedExceptionsPredicate(Class ...ignoreExceptions) { return exceptionPredicate(ignoreExceptions) .map(Predicate::negate); } diff --git a/resilience4j-core/src/test/java/io/github/resilience4j/core/predicate/PredicateCreatorTest.java b/resilience4j-core/src/test/java/io/github/resilience4j/core/predicate/PredicateCreatorTest.java index f2f8667921..faa449a504 100644 --- a/resilience4j-core/src/test/java/io/github/resilience4j/core/predicate/PredicateCreatorTest.java +++ b/resilience4j-core/src/test/java/io/github/resilience4j/core/predicate/PredicateCreatorTest.java @@ -12,7 +12,7 @@ public class PredicateCreatorTest { @Test public void buildRecordExceptionsPredicate() { - Optional> optionalPredicate = PredicateCreator.createRecordExceptionsPredicate(RuntimeException.class); + Optional> optionalPredicate = PredicateCreator.createExceptionsPredicate(RuntimeException.class, IOException.class); then(optionalPredicate).isNotEmpty(); Predicate predicate = optionalPredicate.get(); @@ -20,13 +20,13 @@ public void buildRecordExceptionsPredicate() { then(predicate.test(new IllegalArgumentException())).isEqualTo(true); then(predicate.test(new Throwable())).isEqualTo(false); then(predicate.test(new Exception())).isEqualTo(false); - then(predicate.test(new IOException())).isEqualTo(false); + then(predicate.test(new IOException())).isEqualTo(true); } @Test public void buildIgnoreExceptionsPredicate() { - Optional> optionalPredicate = PredicateCreator.createIgnoreExceptionsPredicate(RuntimeException.class); + Optional> optionalPredicate = PredicateCreator.createNegatedExceptionsPredicate(RuntimeException.class, BusinessException.class); then(optionalPredicate).isNotEmpty(); Predicate predicate = optionalPredicate.get(); @@ -35,5 +35,9 @@ public void buildIgnoreExceptionsPredicate() { then(predicate.test(new Throwable())).isEqualTo(true); then(predicate.test(new Exception())).isEqualTo(true); then(predicate.test(new IOException())).isEqualTo(true); + then(predicate.test(new BusinessException())).isEqualTo(false); + } + + private class BusinessException extends Exception { } } diff --git a/resilience4j-ratpack/src/test/groovy/io/github/resilience4j/ratpack/Resilience4jModuleSpec.groovy b/resilience4j-ratpack/src/test/groovy/io/github/resilience4j/ratpack/Resilience4jModuleSpec.groovy index 55bec823a9..9a63bc61a9 100644 --- a/resilience4j-ratpack/src/test/groovy/io/github/resilience4j/ratpack/Resilience4jModuleSpec.groovy +++ b/resilience4j-ratpack/src/test/groovy/io/github/resilience4j/ratpack/Resilience4jModuleSpec.groovy @@ -98,7 +98,7 @@ class Resilience4jModuleSpec extends Specification { assert waitDurationInOpenState == Duration.ofMillis(5000) assert failureRateThreshold == 60 assert automaticTransitionFromOpenToHalfOpenEnabled - assert recordFailurePredicate.test(new Exception()) + assert recordExceptionPredicate.test(new Exception()) it } } @@ -169,8 +169,8 @@ class Resilience4jModuleSpec extends Specification { assert waitDurationInOpenState == Duration.ofMillis(1000) assert failureRateThreshold == 60 assert automaticTransitionFromOpenToHalfOpenEnabled - assert recordFailurePredicate.test(new DummyException1("test")) - assert recordFailurePredicate.test(new DummyException2("test")) + assert recordExceptionPredicate.test(new DummyException1("test")) + assert recordExceptionPredicate.test(new DummyException2("test")) it } def test2 = circuitBreakerRegistry.circuitBreaker('test2') @@ -181,8 +181,8 @@ class Resilience4jModuleSpec extends Specification { assert waitDurationInOpenState == Duration.ofMillis(5000) assert failureRateThreshold == 60 assert automaticTransitionFromOpenToHalfOpenEnabled - assert recordFailurePredicate.test(new DummyException1("test")) - assert !recordFailurePredicate.test(new DummyException2("test")) + assert recordExceptionPredicate.test(new DummyException1("test")) + assert !recordExceptionPredicate.test(new DummyException2("test")) it } // test default @@ -195,8 +195,8 @@ class Resilience4jModuleSpec extends Specification { assert waitDurationInOpenState == Duration.ofMillis(1000) assert failureRateThreshold == 60 assert automaticTransitionFromOpenToHalfOpenEnabled - assert recordFailurePredicate.test(new DummyException1("test")) - assert recordFailurePredicate.test(new DummyException2("test")) + assert recordExceptionPredicate.test(new DummyException1("test")) + assert recordExceptionPredicate.test(new DummyException2("test")) it } } diff --git a/resilience4j-retry/src/main/java/io/github/resilience4j/retry/RetryConfig.java b/resilience4j-retry/src/main/java/io/github/resilience4j/retry/RetryConfig.java index 9ded87babd..627593fbf9 100644 --- a/resilience4j-retry/src/main/java/io/github/resilience4j/retry/RetryConfig.java +++ b/resilience4j-retry/src/main/java/io/github/resilience4j/retry/RetryConfig.java @@ -19,13 +19,13 @@ package io.github.resilience4j.retry; +import io.github.resilience4j.core.lang.Nullable; +import io.github.resilience4j.core.predicate.PredicateCreator; + import java.time.Duration; import java.util.function.Function; import java.util.function.Predicate; -import io.github.resilience4j.core.lang.Nullable; -import io.github.resilience4j.core.predicate.PredicateCreator; - public class RetryConfig { private static final int DEFAULT_MAX_ATTEMPTS = 3; @@ -250,12 +250,12 @@ public RetryConfig build() { private Predicate createExceptionPredicate() { return createRetryOnExceptionPredicate() - .and(PredicateCreator.createIgnoreExceptionsPredicate(ignoreExceptions) + .and(PredicateCreator.createNegatedExceptionsPredicate(ignoreExceptions) .orElse(DEFAULT_RECORD_FAILURE_PREDICATE)); } private Predicate createRetryOnExceptionPredicate() { - return PredicateCreator.createRecordExceptionsPredicate(retryExceptions) + return PredicateCreator.createExceptionsPredicate(retryExceptions) .map(predicate -> retryOnExceptionPredicate != null ? predicate.or(retryOnExceptionPredicate) : predicate) .orElseGet(() -> retryOnExceptionPredicate != null ? retryOnExceptionPredicate : DEFAULT_RECORD_FAILURE_PREDICATE); } diff --git a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java index 161945ed22..cee8958dc2 100644 --- a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java +++ b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java @@ -103,8 +103,8 @@ public void testCircuitBreakerAutoConfiguration() throws IOException { ResponseEntity circuitBreakerEventList = restTemplate.getForEntity("/circuitbreaker/events", CircuitBreakerEventsEndpointResponse.class); assertThat(circuitBreakerEventList.getBody().getCircuitBreakerEvents()).hasSize(2); - assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordFailurePredicate().test(new RecordedException())).isTrue(); - assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordFailurePredicate().test(new IgnoredException())).isFalse(); + assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordExceptionPredicate().test(new RecordedException())).isTrue(); + assertThat(circuitBreaker.getCircuitBreakerConfig().getIgnoreExceptionPredicate().test(new IgnoredException())).isTrue(); // expect no health indicator for backendB, as it is disabled via properties ResponseEntity healthResponse = restTemplate.getForEntity("/health", String.class); @@ -115,7 +115,7 @@ public void testCircuitBreakerAutoConfiguration() throws IOException { // Verify that an exception for which setRecordFailurePredicate returns false and it is not included in // setRecordExceptions evaluates to false. - assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordFailurePredicate().test(new Exception())).isFalse(); + assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordExceptionPredicate().test(new Exception())).isFalse(); assertThat(circuitBreakerAspect.getOrder()).isEqualTo(400); diff --git a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java index 64d9183d9c..39e2cae527 100644 --- a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java +++ b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java @@ -75,7 +75,7 @@ public void testCircuitBreakerAutoConfiguration() throws IOException { try { dummyService.doSomething(true); } catch (IOException ex) { - // Do nothing. The IOException is recorded by the CircuitBreaker as part of the setRecordFailurePredicate as a failure. + // Do nothing. The IOException is recorded by the CircuitBreaker as part of the recordFailurePredicate as a failure. } // The invocation is recorded by the CircuitBreaker as a success. dummyService.doSomething(false); @@ -116,12 +116,12 @@ public void testCircuitBreakerAutoConfiguration() throws IOException { assertThat(healthResponse.getBody().getDetails().get("backendSharedBCircuitBreaker")).isNotNull(); assertThat(healthResponse.getBody().getDetails().get("dynamicBackend")).isNull(); - assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordFailurePredicate().test(new RecordedException())).isTrue(); - assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordFailurePredicate().test(new IgnoredException())).isFalse(); + assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordExceptionPredicate().test(new RecordedException())).isTrue(); + assertThat(circuitBreaker.getCircuitBreakerConfig().getIgnoreExceptionPredicate().test(new IgnoredException())).isTrue(); // Verify that an exception for which setRecordFailurePredicate returns false and it is not included in // setRecordExceptions evaluates to false. - assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordFailurePredicate().test(new Exception())).isFalse(); + assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordExceptionPredicate().test(new Exception())).isFalse(); assertThat(circuitBreakerAspect.getOrder()).isEqualTo(400);