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 02ba7b6941..35a138ed64 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 @@ -167,6 +167,7 @@ private void handleThrowable(long durationInNanos, Predicate recordFa publishCircuitErrorEvent(name, durationInNanos, throwable); stateReference.get().onError(throwable); } else { + releasePermission(); publishCircuitIgnoredErrorEvent(name, durationInNanos, throwable); } } 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 a70873dba0..afbb0b5d9c 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 @@ -314,6 +314,38 @@ public void shouldForceOpenCircuitBreaker() { assertCircuitBreakerMetricsEqualTo(-1f, 0, 0, 4, 0, 2L); } + @Test + public void shouldReleasePermissionWhenExceptionIgnored() { + circuitBreaker.transitionToOpenState(); + circuitBreaker.transitionToHalfOpenState(); + assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.HALF_OPEN); + + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onSuccess(0); // Should create a CircuitBreakerOnSuccessEvent + assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.HALF_OPEN); + assertCircuitBreakerMetricsEqualTo(-1f, 1, 1, 4, 0, 0L); + + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onSuccess(0); // Should create a CircuitBreakerOnSuccessEvent + assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.HALF_OPEN); + assertCircuitBreakerMetricsEqualTo(-1f, 2, 2, 4, 0, 0L); + + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onSuccess(0); // Should create a CircuitBreakerOnSuccessEvent + assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.HALF_OPEN); + assertCircuitBreakerMetricsEqualTo(-1f, 3, 3, 4, 0, 0L); + + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onError(0, new NumberFormatException()); // Should create a CircuitBreakerOnErrorEvent + assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.HALF_OPEN); + assertCircuitBreakerMetricsEqualTo(-1f, 3, 3, 4, 0, 0L); + + assertThat(circuitBreaker.tryAcquirePermission()).isEqualTo(true); + circuitBreaker.onError(0, new RuntimeException()); // Should create a CircuitBreakerOnErrorEvent + assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); + assertCircuitBreakerMetricsEqualTo(-1f, 3, 4, 5, 1, 0L); + } + private void assertCircuitBreakerMetricsEqualTo(Float expectedFailureRate, Integer expectedSuccessCalls, Integer expectedBufferedCalls, Integer expectedMaxBufferedCalls, Integer expectedFailedCalls, Long expectedNotPermittedCalls) { final CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getFailureRate()).isEqualTo(expectedFailureRate);