Skip to content

Commit 31f313a

Browse files
author
Robert Winkler
committed
Fixes issue ReactiveX#6: The CB changes to OPEN state when the failure rate is above (>) the threshold, but it should be above or equal (>=).
1 parent 3d09b8f commit 31f313a

File tree

6 files changed

+43
-11
lines changed

6 files changed

+43
-11
lines changed

README.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ After the time duration has elapsed, the CircuitBreaker state changes from `OPEN
4747

4848
== Usage guide
4949

50-
See http://javaslang.github.io/javaslang-circuitbreaker/0.4.0/[User Guide].
50+
See http://javaslang.github.io/javaslang-circuitbreaker/0.5.0/[User Guide].
5151

5252
== Companies who use javaslang-circuitbreaker
5353

RELEASENOTES.adoc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,9 @@
4545
=== Version 0.3.4
4646
* Added a Metrics interface to the CircuitBreaker which allows to retrieve current statistics like failureRate and number of buffer calls.
4747

48-
== Version 0.40
49-
* Updated javaslang from 2.0.0-RC4 to 2.0.1
48+
== Version 0.4.0
49+
* Updated javaslang from 2.0.0-RC4 to 2.0.1
50+
51+
== Version 0.5.0
52+
* Updated javaslang from 2.0.1 to 2.0.2
53+
* Fixed issue #6: The CB changes to OPEN state when the failure rate is above (>) the threshold, but it should be above or equal (>=).

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ apply plugin: 'org.ajoberstar.github-pages'
2929

3030
group = 'io.github.robwin'
3131
description = 'A CircuitBreaker pattern implementation for Java 8 and functional programming.'
32-
version = '0.4.0'
32+
version = '0.5.0'
3333

3434
tasks.withType(JavaCompile) {
3535
sourceCompatibility = "1.8"
@@ -60,7 +60,7 @@ jmh {
6060
}
6161

6262
dependencies {
63-
compile "io.javaslang:javaslang:2.0.1"
63+
compile "io.javaslang:javaslang:2.0.2"
6464
compile "org.slf4j:slf4j-api:1.7.13"
6565
testCompile "io.dropwizard.metrics:metrics-core:3.1.2"
6666
testCompile "junit:junit:4.11"

src/main/java/javaslang/circuitbreaker/internal/ClosedState.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ void recordSuccess() {
5858
* @param currentFailureRate the current failure rate
5959
*/
6060
private void checkFailureRate(float currentFailureRate) {
61-
if (currentFailureRate > failureRateThreshold) {
61+
if (currentFailureRate >= failureRateThreshold) {
6262
// Transition the state machine to OPEN state, because the failure rate is above the threshold
6363
stateMachine.transitionToOpenState(CircuitBreaker.StateTransition.CLOSED_TO_OPEN, circuitBreakerMetrics);
6464
}

src/main/java/javaslang/circuitbreaker/internal/HalfOpenState.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ void recordSuccess() {
6262
*/
6363
private void checkFailureRate(float currentFailureRate) {
6464
if(currentFailureRate != -1){
65-
if(currentFailureRate > failureRateThreshold) {
65+
if(currentFailureRate >= failureRateThreshold) {
6666
stateMachine.transitionToOpenState(CircuitBreaker.StateTransition.HALF_OPEN_TO_OPEN, circuitBreakerMetrics);
6767
}else{
6868
stateMachine.transitionToClosedState(CircuitBreaker.StateTransition.HALF_OPEN_TO_CLOSED);

src/test/java/javaslang/circuitbreaker/internal/CircuitBreakerStateMachineTest.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@
2020

2121
import javaslang.circuitbreaker.CircuitBreaker;
2222
import javaslang.circuitbreaker.CircuitBreakerConfig;
23+
import javaslang.circuitbreaker.CircuitBreakerRegistry;
2324
import org.junit.Before;
2425
import org.junit.Test;
2526

2627
import java.time.Duration;
28+
import java.util.concurrent.TimeUnit;
2729

2830
import static java.lang.Thread.sleep;
2931
import static org.assertj.core.api.BDDAssertions.assertThat;
32+
import static org.junit.Assert.assertFalse;
33+
import static org.junit.Assert.assertTrue;
3034

3135
public class CircuitBreakerStateMachineTest {
3236

@@ -37,7 +41,7 @@ public void setUp(){
3741
circuitBreaker = new CircuitBreakerStateMachine("testName", CircuitBreakerConfig.custom()
3842
.failureRateThreshold(50)
3943
.ringBufferSizeInClosedState(5)
40-
.ringBufferSizeInHalfOpenState(2)
44+
.ringBufferSizeInHalfOpenState(3)
4145
.waitDurationInOpenState(Duration.ofSeconds(1))
4246
.build());
4347
}
@@ -121,13 +125,16 @@ public void testCircuitBreakerStateMachine() throws InterruptedException {
121125

122126
// Call 2 is a failure
123127
circuitBreaker.recordFailure(new RuntimeException());
128+
// Call 3 is a success
129+
circuitBreaker.recordSuccess();
130+
124131
// The ring buffer is filled and the failure rate is above 50%
125132
// The state machine transitions back to OPEN state
126133
assertThat(circuitBreaker.isCallPermitted()).isEqualTo(false);
127134
assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.OPEN);
128-
assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls()).isEqualTo(2);
135+
assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls()).isEqualTo(3);
129136
assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls()).isEqualTo(2);
130-
assertThat(circuitBreaker.getMetrics().getFailureRate()).isEqualTo(100f);
137+
assertThat(circuitBreaker.getMetrics().getFailureRate()).isGreaterThan(50f);
131138

132139
sleep(1300);
133140

@@ -145,12 +152,33 @@ public void testCircuitBreakerStateMachine() throws InterruptedException {
145152

146153
// Call 2 is a success
147154
circuitBreaker.recordSuccess();
148-
// The ring buffer is filled and the failure rate is equal to 50%
155+
// Call 3 is a success
156+
circuitBreaker.recordSuccess();
157+
158+
// The ring buffer is filled and the failure rate is below 50%
149159
// The state machine transitions back to CLOSED state
150160
assertThat(circuitBreaker.isCallPermitted()).isEqualTo(true);
151161
assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED);
152162
assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls()).isEqualTo(0);
153163
assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls()).isEqualTo(0);
154164
assertThat(circuitBreaker.getMetrics().getFailureRate()).isEqualTo(-1f);
155165
}
166+
167+
@Test
168+
public void testCircuitBreakerBehaviour() throws InterruptedException {
169+
CircuitBreakerRegistry registry = CircuitBreakerRegistry.ofDefaults();
170+
int times = 3;
171+
int waitSeconds = 2;
172+
CircuitBreakerConfig config = CircuitBreakerConfig.custom().ringBufferSizeInHalfOpenState(times)
173+
.failureRateThreshold(100).ringBufferSizeInClosedState(times)
174+
.waitDurationInOpenState(Duration.ofSeconds(waitSeconds)).build();
175+
CircuitBreaker circuitBreaker = registry.circuitBreaker("something", config);
176+
for (int i = 0; i < times; i++) {
177+
assertTrue("Circuit-breaker call should be permitted", circuitBreaker.isCallPermitted());
178+
circuitBreaker.recordFailure(new RuntimeException("Fail! " + i));
179+
}
180+
assertFalse("Circuit-breaker call should not be permitted", circuitBreaker.isCallPermitted());
181+
Thread.sleep(TimeUnit.SECONDS.toMillis(waitSeconds));
182+
assertTrue("Circuit-breaker call should be permitted", circuitBreaker.isCallPermitted());
183+
}
156184
}

0 commit comments

Comments
 (0)