Skip to content

Commit 13ef4be

Browse files
darkospoljaricRobWin
authored andcommitted
Added circuit breaker status to health (ReactiveX#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.
1 parent 8cc96a0 commit 13ef4be

File tree

4 files changed

+105
-55
lines changed

4 files changed

+105
-55
lines changed

resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public class CircuitBreakerHealthIndicator implements HealthIndicator {
3535
private static final String FAILED_CALLS = "failedCalls";
3636
private static final String NOT_PERMITTED = "notPermittedCalls";
3737
private static final String MAX_BUFFERED_CALLS = "maxBufferedCalls";
38+
private static final String STATE = "state";
3839
private CircuitBreaker circuitBreaker;
3940

4041
public CircuitBreakerHealthIndicator(CircuitBreaker circuitBreaker) {
@@ -69,7 +70,8 @@ private Health.Builder addDetails(Health.Builder builder, CircuitBreaker circuit
6970
.withDetail(MAX_BUFFERED_CALLS, metrics.getMaxNumberOfBufferedCalls())
7071
.withDetail(BUFFERED_CALLS, metrics.getNumberOfBufferedCalls())
7172
.withDetail(FAILED_CALLS, metrics.getNumberOfFailedCalls())
72-
.withDetail(NOT_PERMITTED, metrics.getNumberOfNotPermittedCalls());
73+
.withDetail(NOT_PERMITTED, metrics.getNumberOfNotPermittedCalls())
74+
.withDetail(STATE, circuitBreaker.getState());
7375
return builder;
7476
}
7577
}

resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
package io.github.resilience4j.circuitbreaker.monitoring.health;
22

3-
import static org.assertj.core.api.BDDAssertions.then;
4-
import static org.mockito.Mockito.mock;
5-
import static org.mockito.Mockito.when;
6-
import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.CLOSED;
7-
import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.HALF_OPEN;
8-
import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.OPEN;
9-
3+
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
4+
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
105
import org.junit.Test;
116
import org.springframework.boot.actuate.health.Health;
127
import org.springframework.boot.actuate.health.Status;
138

14-
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
15-
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
16-
179
import java.util.AbstractMap.SimpleEntry;
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
13+
import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.*;
14+
import static org.assertj.core.api.BDDAssertions.then;
15+
import static org.mockito.Mockito.mock;
16+
import static org.mockito.Mockito.when;
1817

1918
/**
2019
* @author bstorozhuk
2120
*/
2221
public class CircuitBreakerHealthIndicatorTest {
22+
2323
@Test
24-
public void health() throws Exception {
24+
public void healthMetricsAndConfig() {
2525
// given
2626
CircuitBreakerConfig config = mock(CircuitBreakerConfig.class);
2727
CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class);
@@ -37,33 +37,56 @@ public void health() throws Exception {
3737
when(metrics.getNumberOfFailedCalls()).thenReturn(20);
3838
when(metrics.getNumberOfNotPermittedCalls()).thenReturn(0L);
3939

40-
4140
when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config);
4241
when(circuitBreaker.getMetrics()).thenReturn(metrics);
4342
when(circuitBreaker.getState()).thenReturn(CLOSED, OPEN, HALF_OPEN, CLOSED);
4443

4544
// then
4645
Health health = healthIndicator.health();
4746
then(health.getStatus()).isEqualTo(Status.UP);
47+
then(health.getDetails())
48+
.contains(
49+
entry("failureRate", "0.2%"),
50+
entry("failureRateThreshold", "0.3%"),
51+
entry("bufferedCalls", 100),
52+
entry("failedCalls", 20),
53+
entry("notPermittedCalls", 0L),
54+
entry("maxBufferedCalls", 100)
55+
);
56+
}
4857

49-
health = healthIndicator.health();
50-
then(health.getStatus()).isEqualTo(Status.DOWN);
58+
@Test
59+
public void testHealthStatus() {
60+
Map<CircuitBreaker.State, Status> expectedStateToStatusMap = new HashMap<>();
61+
expectedStateToStatusMap.put(OPEN, Status.DOWN);
62+
expectedStateToStatusMap.put(HALF_OPEN, Status.UNKNOWN);
63+
expectedStateToStatusMap.put(CLOSED, Status.UP);
5164

52-
health = healthIndicator.health();
53-
then(health.getStatus()).isEqualTo(Status.UNKNOWN);
65+
// given
66+
CircuitBreakerConfig config = mock(CircuitBreakerConfig.class);
67+
CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class);
68+
CircuitBreaker circuitBreaker = mock(CircuitBreaker.class);
5469

55-
health = healthIndicator.health();
56-
then(health.getStatus()).isEqualTo(Status.UP);
70+
when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config);
71+
when(circuitBreaker.getMetrics()).thenReturn(metrics);
5772

73+
expectedStateToStatusMap.forEach((state, status) -> assertStatusForGivenState(circuitBreaker, state, status));
74+
}
75+
76+
private void assertStatusForGivenState(CircuitBreaker circuitBreaker, CircuitBreaker.State givenState, Status expectedStatus) {
77+
// given
78+
when(circuitBreaker.getState()).thenReturn(givenState);
79+
CircuitBreakerHealthIndicator healthIndicator = new CircuitBreakerHealthIndicator(circuitBreaker);
80+
81+
// when
82+
Health health = healthIndicator.health();
83+
84+
// then
85+
then(health.getStatus()).isEqualTo(expectedStatus);
5886
then(health.getDetails())
59-
.contains(
60-
entry("failureRate", "0.2%"),
61-
entry("failureRateThreshold", "0.3%"),
62-
entry("bufferedCalls", 100),
63-
entry("failedCalls", 20),
64-
entry("notPermittedCalls", 0L),
65-
entry("maxBufferedCalls", 100)
66-
);
87+
.contains(
88+
entry("state", givenState)
89+
);
6790
}
6891

6992
private SimpleEntry<String, ?> entry(String key, Object value) {

resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public class CircuitBreakerHealthIndicator implements HealthIndicator {
3535
private static final String FAILED_CALLS = "failedCalls";
3636
private static final String NOT_PERMITTED = "notPermittedCalls";
3737
private static final String MAX_BUFFERED_CALLS = "maxBufferedCalls";
38+
private static final String STATE = "state";
3839
private CircuitBreaker circuitBreaker;
3940

4041
public CircuitBreakerHealthIndicator(CircuitBreaker circuitBreaker) {
@@ -69,7 +70,8 @@ private Health.Builder addDetails(Health.Builder builder, CircuitBreaker circuit
6970
.withDetail(MAX_BUFFERED_CALLS, metrics.getMaxNumberOfBufferedCalls())
7071
.withDetail(BUFFERED_CALLS, metrics.getNumberOfBufferedCalls())
7172
.withDetail(FAILED_CALLS, metrics.getNumberOfFailedCalls())
72-
.withDetail(NOT_PERMITTED, metrics.getNumberOfNotPermittedCalls());
73+
.withDetail(NOT_PERMITTED, metrics.getNumberOfNotPermittedCalls())
74+
.withDetail(STATE, circuitBreaker.getState());
7375
return builder;
7476
}
7577
}

resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
package io.github.resilience4j.circuitbreaker.monitoring.health;
22

3-
import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.CLOSED;
4-
import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.HALF_OPEN;
5-
import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.OPEN;
6-
import static org.assertj.core.api.BDDAssertions.then;
7-
import static org.mockito.Mockito.mock;
8-
import static org.mockito.Mockito.when;
9-
10-
import java.util.AbstractMap.SimpleEntry;
11-
3+
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
4+
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
125
import org.junit.Test;
136
import org.springframework.boot.actuate.health.Health;
147
import org.springframework.boot.actuate.health.Status;
158

16-
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
17-
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
9+
import java.util.AbstractMap.SimpleEntry;
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
13+
import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.*;
14+
import static org.assertj.core.api.BDDAssertions.then;
15+
import static org.mockito.Mockito.mock;
16+
import static org.mockito.Mockito.when;
1817

1918
/**
2019
* @author bstorozhuk
2120
*/
2221
public class CircuitBreakerHealthIndicatorTest {
22+
2323
@Test
24-
public void health() throws Exception {
24+
public void healthMetricsAndConfig() {
2525
// given
2626
CircuitBreakerConfig config = mock(CircuitBreakerConfig.class);
2727
CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class);
@@ -37,33 +37,56 @@ public void health() throws Exception {
3737
when(metrics.getNumberOfFailedCalls()).thenReturn(20);
3838
when(metrics.getNumberOfNotPermittedCalls()).thenReturn(0L);
3939

40-
4140
when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config);
4241
when(circuitBreaker.getMetrics()).thenReturn(metrics);
4342
when(circuitBreaker.getState()).thenReturn(CLOSED, OPEN, HALF_OPEN, CLOSED);
4443

4544
// then
4645
Health health = healthIndicator.health();
4746
then(health.getStatus()).isEqualTo(Status.UP);
47+
then(health.getDetails())
48+
.contains(
49+
entry("failureRate", "0.2%"),
50+
entry("failureRateThreshold", "0.3%"),
51+
entry("bufferedCalls", 100),
52+
entry("failedCalls", 20),
53+
entry("notPermittedCalls", 0L),
54+
entry("maxBufferedCalls", 100)
55+
);
56+
}
4857

49-
health = healthIndicator.health();
50-
then(health.getStatus()).isEqualTo(Status.DOWN);
58+
@Test
59+
public void testHealthStatus() {
60+
Map<CircuitBreaker.State, Status> expectedStateToStatusMap = new HashMap<>();
61+
expectedStateToStatusMap.put(OPEN, Status.DOWN);
62+
expectedStateToStatusMap.put(HALF_OPEN, Status.UNKNOWN);
63+
expectedStateToStatusMap.put(CLOSED, Status.UP);
5164

52-
health = healthIndicator.health();
53-
then(health.getStatus()).isEqualTo(Status.UNKNOWN);
65+
// given
66+
CircuitBreakerConfig config = mock(CircuitBreakerConfig.class);
67+
CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class);
68+
CircuitBreaker circuitBreaker = mock(CircuitBreaker.class);
5469

55-
health = healthIndicator.health();
56-
then(health.getStatus()).isEqualTo(Status.UP);
70+
when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config);
71+
when(circuitBreaker.getMetrics()).thenReturn(metrics);
5772

73+
expectedStateToStatusMap.forEach((state, status) -> assertStatusForGivenState(circuitBreaker, state, status));
74+
}
75+
76+
private void assertStatusForGivenState(CircuitBreaker circuitBreaker, CircuitBreaker.State givenState, Status expectedStatus) {
77+
// given
78+
when(circuitBreaker.getState()).thenReturn(givenState);
79+
CircuitBreakerHealthIndicator healthIndicator = new CircuitBreakerHealthIndicator(circuitBreaker);
80+
81+
// when
82+
Health health = healthIndicator.health();
83+
84+
// then
85+
then(health.getStatus()).isEqualTo(expectedStatus);
5886
then(health.getDetails())
59-
.contains(
60-
entry("failureRate", "0.2%"),
61-
entry("failureRateThreshold", "0.3%"),
62-
entry("bufferedCalls", 100),
63-
entry("failedCalls", 20),
64-
entry("notPermittedCalls", 0L),
65-
entry("maxBufferedCalls", 100)
66-
);
87+
.contains(
88+
entry("state", givenState)
89+
);
6790
}
6891

6992
private SimpleEntry<String, ?> entry(String key, Object value) {

0 commit comments

Comments
 (0)