From 13ef4bec8e653c86df559fec80f34b5461339055 Mon Sep 17 00:00:00 2001 From: darkospoljaric Date: Tue, 26 Mar 2019 15:27:37 +0100 Subject: [PATCH] Added circuit breaker status to health (#371) * Added circuit breaker status to health * Added state asserts to CircuitBreakerHealthIndicatorTest * Updated health details to include circuit breaker state for resilience4j-spring-boot * Refactored CircuitBreakerHealthIndicator and CircuitBreakerHealthIndicatorTest for spring-boot and spring-boot2 modules. --- .../health/CircuitBreakerHealthIndicator.java | 4 +- .../CircuitBreakerHealthIndicatorTest.java | 75 +++++++++++------- .../health/CircuitBreakerHealthIndicator.java | 4 +- .../CircuitBreakerHealthIndicatorTest.java | 77 ++++++++++++------- 4 files changed, 105 insertions(+), 55 deletions(-) diff --git a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java index edc25f772f..a89738cfc0 100644 --- a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java +++ b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java @@ -35,6 +35,7 @@ public class CircuitBreakerHealthIndicator implements HealthIndicator { private static final String FAILED_CALLS = "failedCalls"; private static final String NOT_PERMITTED = "notPermittedCalls"; private static final String MAX_BUFFERED_CALLS = "maxBufferedCalls"; + private static final String STATE = "state"; private CircuitBreaker circuitBreaker; public CircuitBreakerHealthIndicator(CircuitBreaker circuitBreaker) { @@ -69,7 +70,8 @@ private Health.Builder addDetails(Health.Builder builder, CircuitBreaker circuit .withDetail(MAX_BUFFERED_CALLS, metrics.getMaxNumberOfBufferedCalls()) .withDetail(BUFFERED_CALLS, metrics.getNumberOfBufferedCalls()) .withDetail(FAILED_CALLS, metrics.getNumberOfFailedCalls()) - .withDetail(NOT_PERMITTED, metrics.getNumberOfNotPermittedCalls()); + .withDetail(NOT_PERMITTED, metrics.getNumberOfNotPermittedCalls()) + .withDetail(STATE, circuitBreaker.getState()); return builder; } } diff --git a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java index ddcfd9c9ae..4f27dba27b 100644 --- a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java +++ b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java @@ -1,27 +1,27 @@ package io.github.resilience4j.circuitbreaker.monitoring.health; -import static org.assertj.core.api.BDDAssertions.then; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.CLOSED; -import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.HALF_OPEN; -import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.OPEN; - +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import org.junit.Test; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; -import io.github.resilience4j.circuitbreaker.CircuitBreaker; -import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; - import java.util.AbstractMap.SimpleEntry; +import java.util.HashMap; +import java.util.Map; + +import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.*; +import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * @author bstorozhuk */ public class CircuitBreakerHealthIndicatorTest { + @Test - public void health() throws Exception { + public void healthMetricsAndConfig() { // given CircuitBreakerConfig config = mock(CircuitBreakerConfig.class); CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class); @@ -37,7 +37,6 @@ public void health() throws Exception { when(metrics.getNumberOfFailedCalls()).thenReturn(20); when(metrics.getNumberOfNotPermittedCalls()).thenReturn(0L); - when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config); when(circuitBreaker.getMetrics()).thenReturn(metrics); when(circuitBreaker.getState()).thenReturn(CLOSED, OPEN, HALF_OPEN, CLOSED); @@ -45,25 +44,49 @@ public void health() throws Exception { // then Health health = healthIndicator.health(); then(health.getStatus()).isEqualTo(Status.UP); + then(health.getDetails()) + .contains( + entry("failureRate", "0.2%"), + entry("failureRateThreshold", "0.3%"), + entry("bufferedCalls", 100), + entry("failedCalls", 20), + entry("notPermittedCalls", 0L), + entry("maxBufferedCalls", 100) + ); + } - health = healthIndicator.health(); - then(health.getStatus()).isEqualTo(Status.DOWN); + @Test + public void testHealthStatus() { + Map expectedStateToStatusMap = new HashMap<>(); + expectedStateToStatusMap.put(OPEN, Status.DOWN); + expectedStateToStatusMap.put(HALF_OPEN, Status.UNKNOWN); + expectedStateToStatusMap.put(CLOSED, Status.UP); - health = healthIndicator.health(); - then(health.getStatus()).isEqualTo(Status.UNKNOWN); + // given + CircuitBreakerConfig config = mock(CircuitBreakerConfig.class); + CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class); + CircuitBreaker circuitBreaker = mock(CircuitBreaker.class); - health = healthIndicator.health(); - then(health.getStatus()).isEqualTo(Status.UP); + when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config); + when(circuitBreaker.getMetrics()).thenReturn(metrics); + expectedStateToStatusMap.forEach((state, status) -> assertStatusForGivenState(circuitBreaker, state, status)); + } + + private void assertStatusForGivenState(CircuitBreaker circuitBreaker, CircuitBreaker.State givenState, Status expectedStatus) { + // given + when(circuitBreaker.getState()).thenReturn(givenState); + CircuitBreakerHealthIndicator healthIndicator = new CircuitBreakerHealthIndicator(circuitBreaker); + + // when + Health health = healthIndicator.health(); + + // then + then(health.getStatus()).isEqualTo(expectedStatus); then(health.getDetails()) - .contains( - entry("failureRate", "0.2%"), - entry("failureRateThreshold", "0.3%"), - entry("bufferedCalls", 100), - entry("failedCalls", 20), - entry("notPermittedCalls", 0L), - entry("maxBufferedCalls", 100) - ); + .contains( + entry("state", givenState) + ); } private SimpleEntry entry(String key, Object value) { diff --git a/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java index edc25f772f..a89738cfc0 100644 --- a/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java +++ b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java @@ -35,6 +35,7 @@ public class CircuitBreakerHealthIndicator implements HealthIndicator { private static final String FAILED_CALLS = "failedCalls"; private static final String NOT_PERMITTED = "notPermittedCalls"; private static final String MAX_BUFFERED_CALLS = "maxBufferedCalls"; + private static final String STATE = "state"; private CircuitBreaker circuitBreaker; public CircuitBreakerHealthIndicator(CircuitBreaker circuitBreaker) { @@ -69,7 +70,8 @@ private Health.Builder addDetails(Health.Builder builder, CircuitBreaker circuit .withDetail(MAX_BUFFERED_CALLS, metrics.getMaxNumberOfBufferedCalls()) .withDetail(BUFFERED_CALLS, metrics.getNumberOfBufferedCalls()) .withDetail(FAILED_CALLS, metrics.getNumberOfFailedCalls()) - .withDetail(NOT_PERMITTED, metrics.getNumberOfNotPermittedCalls()); + .withDetail(NOT_PERMITTED, metrics.getNumberOfNotPermittedCalls()) + .withDetail(STATE, circuitBreaker.getState()); return builder; } } diff --git a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java index cfca0c053f..4f27dba27b 100644 --- a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java +++ b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java @@ -1,27 +1,27 @@ package io.github.resilience4j.circuitbreaker.monitoring.health; -import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.CLOSED; -import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.HALF_OPEN; -import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.OPEN; -import static org.assertj.core.api.BDDAssertions.then; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.AbstractMap.SimpleEntry; - +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import org.junit.Test; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; -import io.github.resilience4j.circuitbreaker.CircuitBreaker; -import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import java.util.AbstractMap.SimpleEntry; +import java.util.HashMap; +import java.util.Map; + +import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.*; +import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * @author bstorozhuk */ public class CircuitBreakerHealthIndicatorTest { + @Test - public void health() throws Exception { + public void healthMetricsAndConfig() { // given CircuitBreakerConfig config = mock(CircuitBreakerConfig.class); CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class); @@ -37,7 +37,6 @@ public void health() throws Exception { when(metrics.getNumberOfFailedCalls()).thenReturn(20); when(metrics.getNumberOfNotPermittedCalls()).thenReturn(0L); - when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config); when(circuitBreaker.getMetrics()).thenReturn(metrics); when(circuitBreaker.getState()).thenReturn(CLOSED, OPEN, HALF_OPEN, CLOSED); @@ -45,25 +44,49 @@ public void health() throws Exception { // then Health health = healthIndicator.health(); then(health.getStatus()).isEqualTo(Status.UP); + then(health.getDetails()) + .contains( + entry("failureRate", "0.2%"), + entry("failureRateThreshold", "0.3%"), + entry("bufferedCalls", 100), + entry("failedCalls", 20), + entry("notPermittedCalls", 0L), + entry("maxBufferedCalls", 100) + ); + } - health = healthIndicator.health(); - then(health.getStatus()).isEqualTo(Status.DOWN); + @Test + public void testHealthStatus() { + Map expectedStateToStatusMap = new HashMap<>(); + expectedStateToStatusMap.put(OPEN, Status.DOWN); + expectedStateToStatusMap.put(HALF_OPEN, Status.UNKNOWN); + expectedStateToStatusMap.put(CLOSED, Status.UP); - health = healthIndicator.health(); - then(health.getStatus()).isEqualTo(Status.UNKNOWN); + // given + CircuitBreakerConfig config = mock(CircuitBreakerConfig.class); + CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class); + CircuitBreaker circuitBreaker = mock(CircuitBreaker.class); - health = healthIndicator.health(); - then(health.getStatus()).isEqualTo(Status.UP); + when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config); + when(circuitBreaker.getMetrics()).thenReturn(metrics); + expectedStateToStatusMap.forEach((state, status) -> assertStatusForGivenState(circuitBreaker, state, status)); + } + + private void assertStatusForGivenState(CircuitBreaker circuitBreaker, CircuitBreaker.State givenState, Status expectedStatus) { + // given + when(circuitBreaker.getState()).thenReturn(givenState); + CircuitBreakerHealthIndicator healthIndicator = new CircuitBreakerHealthIndicator(circuitBreaker); + + // when + Health health = healthIndicator.health(); + + // then + then(health.getStatus()).isEqualTo(expectedStatus); then(health.getDetails()) - .contains( - entry("failureRate", "0.2%"), - entry("failureRateThreshold", "0.3%"), - entry("bufferedCalls", 100), - entry("failedCalls", 20), - entry("notPermittedCalls", 0L), - entry("maxBufferedCalls", 100) - ); + .contains( + entry("state", givenState) + ); } private SimpleEntry entry(String key, Object value) {