Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added circuit breaker status to health (ReactiveX#371)
Browse files Browse the repository at this point in the history
* 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.
darkospoljaric authored and RobWin committed Mar 26, 2019
1 parent 8cc96a0 commit 13ef4be
Showing 4 changed files with 105 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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,33 +37,56 @@ 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);

// 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<CircuitBreaker.State, Status> 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<String, ?> entry(String key, Object value) {
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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,33 +37,56 @@ 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);

// 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<CircuitBreaker.State, Status> 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<String, ?> entry(String key, Object value) {

0 comments on commit 13ef4be

Please sign in to comment.