From 07b811196b6e3d9bb2f0d74a7633cc6ad9c215c3 Mon Sep 17 00:00:00 2001 From: Eric Zhao Date: Mon, 18 May 2020 23:00:59 +0800 Subject: [PATCH] Update test cases for circuit breaking Signed-off-by: Eric Zhao --- .../CircuitBreakingIntegrationTest.java | 213 ++++++++++++++++++ .../block/degrade/DegradeRuleManagerTest.java | 45 +++- .../slots/block/degrade/DegradeRuleTest.java | 51 +++++ .../slots/block/degrade/DegradeTest.java | 182 --------------- .../ExceptionCircuitBreakerTest.java | 111 +++++++++ 5 files changed, 417 insertions(+), 185 deletions(-) create mode 100755 sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/CircuitBreakingIntegrationTest.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleTest.java delete mode 100755 sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeTest.java create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/ExceptionCircuitBreakerTest.java diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/CircuitBreakingIntegrationTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/CircuitBreakingIntegrationTest.java new file mode 100755 index 00000000..797943fe --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/CircuitBreakingIntegrationTest.java @@ -0,0 +1,213 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.csp.sentinel.slots.block.degrade; + +import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.Tracer; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker.State; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStateChangeObserver; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry; +import com.alibaba.csp.sentinel.test.AbstractTimeBasedTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.ThreadLocalRandom; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; + +/** + * @author jialiang.linjl + * @author Eric Zhao + */ +public class CircuitBreakingIntegrationTest extends AbstractTimeBasedTest { + + @Before + public void setUp() { + DegradeRuleManager.loadRules(new ArrayList()); + } + + @After + public void tearDown() throws Exception { + DegradeRuleManager.loadRules(new ArrayList()); + } + + private boolean entryAndSleepFor(String res, int sleepMs) { + Entry entry = null; + try { + entry = SphU.entry(res); + sleep(sleepMs); + } catch (BlockException ex) { + return false; + } catch (Exception ex) { + Tracer.traceEntry(ex, entry); + } finally { + if (entry != null) { + entry.exit(); + } + } + return true; + } + + private boolean entryWithErrorIfPresent(String res, Exception ex) { + Entry entry = null; + try { + entry = SphU.entry(res); + if (ex != null) { + Tracer.traceEntry(ex, entry); + } + sleep(ThreadLocalRandom.current().nextInt(5, 10)); + } catch (BlockException b) { + return false; + } finally { + if (entry != null) { + entry.exit(); + } + } + return true; + } + + @Test + public void testSlowRequestMode() throws Exception { + CircuitBreakerStateChangeObserver observer = mock(CircuitBreakerStateChangeObserver.class); + setCurrentMillis(System.currentTimeMillis() / 1000 * 1000); + int retryTimeoutSec = 5; + int maxRt = 50; + int statIntervalMs = 20000; + int minRequestAmount = 10; + String res = "CircuitBreakingIntegrationTest_testSlowRequestMode"; + EventObserverRegistry.getInstance().addStateChangeObserver(res, observer); + DegradeRuleManager.loadRules(Arrays.asList( + new DegradeRule(res).setTimeWindow(retryTimeoutSec).setCount(maxRt) + .setStatIntervalMs(statIntervalMs).setMinRequestAmount(minRequestAmount) + .setSlowRatioThreshold(0.8d).setGrade(0) + )); + + // Try first N requests where N = minRequestAmount. + for (int i = 0; i < minRequestAmount; i++) { + if (i < 7) { + assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); + } else { + assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(-20, -10))); + } + } + + // Till now slow ratio should be 70%. + assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); + assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); + assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); + assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); + assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); + assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); + // Circuit breaker has transformed to OPEN since here. + verify(observer) + .onStateChange(eq(State.CLOSED), eq(State.OPEN), any(DegradeRule.class), anyDouble()); + assertEquals(State.OPEN, DegradeRuleManager.getCircuitBreakers(res).get(0).currentState()); + assertFalse(entryAndSleepFor(res, 1)); + + sleepSecond(1); + assertFalse(entryAndSleepFor(res, 1)); + sleepSecond(retryTimeoutSec); + // Test HALF-OPEN to OPEN. + assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); + + verify(observer) + .onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class)); + verify(observer) + .onStateChange(eq(State.HALF_OPEN), eq(State.OPEN), any(DegradeRule.class), anyDouble()); + // Wait for next retry timeout; + reset(observer); + sleepSecond(retryTimeoutSec + 1); + assertTrue(entryAndSleepFor(res, maxRt - ThreadLocalRandom.current().nextInt(10, 20))); + verify(observer) + .onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class)); + verify(observer) + .onStateChange(eq(State.HALF_OPEN), eq(State.CLOSED), any(DegradeRule.class), nullable(Double.class)); + // Now circuit breaker has been closed. + assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); + + EventObserverRegistry.getInstance().removeStateChangeObserver(res); + } + + @Test + public void testExceptionRatioMode() throws Exception { + CircuitBreakerStateChangeObserver observer = mock(CircuitBreakerStateChangeObserver.class); + setCurrentMillis(System.currentTimeMillis() / 1000 * 1000); + int retryTimeoutSec = 5; + double maxRatio = 0.5; + int statIntervalMs = 25000; + final int minRequestAmount = 10; + String res = "CircuitBreakingIntegrationTest_testExceptionRatioMode"; + EventObserverRegistry.getInstance().addStateChangeObserver(res, observer); + DegradeRuleManager.loadRules(Arrays.asList( + new DegradeRule(res).setTimeWindow(retryTimeoutSec).setCount(maxRatio) + .setStatIntervalMs(statIntervalMs).setMinRequestAmount(minRequestAmount) + .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) + )); + + // Try first N requests where N = minRequestAmount. + for (int i = 0; i < minRequestAmount - 1; i++) { + if (i < 6) { + assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException())); + } else { + assertTrue(entryWithErrorIfPresent(res, null)); + } + } + + // Till now slow ratio should be 60%. + assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException())); + // Circuit breaker has transformed to OPEN since here. + assertEquals(State.OPEN, DegradeRuleManager.getCircuitBreakers(res).get(0).currentState()); + assertFalse(entryWithErrorIfPresent(res, null)); + + sleepSecond(2); + assertFalse(entryWithErrorIfPresent(res, null)); + sleepSecond(retryTimeoutSec); + // Test HALF-OPEN to OPEN. + assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException())); + verify(observer) + .onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class)); + verify(observer) + .onStateChange(eq(State.HALF_OPEN), eq(State.OPEN), any(DegradeRule.class), anyDouble()); + // Wait for next retry timeout; + reset(observer); + sleepSecond(retryTimeoutSec + 1); + assertTrue(entryWithErrorIfPresent(res, null)); + verify(observer) + .onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class)); + verify(observer) + .onStateChange(eq(State.HALF_OPEN), eq(State.CLOSED), any(DegradeRule.class), nullable(Double.class)); + // Now circuit breaker has been closed. + assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException())); + + EventObserverRegistry.getInstance().removeStateChangeObserver(res); + } + + @Test + public void testExceptionCountMode() throws Throwable { + // TODO + } + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManagerTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManagerTest.java index 0d73610b..fa7c6358 100644 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManagerTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManagerTest.java @@ -15,8 +15,14 @@ */ package com.alibaba.csp.sentinel.slots.block.degrade; -import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import java.util.ArrayList; +import java.util.Arrays; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker; + +import org.junit.After; +import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; @@ -28,6 +34,32 @@ import static org.junit.Assert.*; */ public class DegradeRuleManagerTest { + @Before + public void setUp() throws Exception { + DegradeRuleManager.loadRules(new ArrayList()); + } + + @After + public void tearDown() throws Exception { + DegradeRuleManager.loadRules(new ArrayList()); + } + + @Test + public void loadSameRuleUseSameCircuitBreaker() { + String resource = "loadSameRuleUseSameCircuitBreaker"; + DegradeRule rule = new DegradeRule(resource) + .setCount(100) + .setSlowRatioThreshold(0.9d) + .setTimeWindow(20) + .setStatIntervalMs(20000); + DegradeRuleManager.loadRules(Arrays.asList(rule)); + CircuitBreaker cb = DegradeRuleManager.getCircuitBreakers(resource).get(0); + + DegradeRuleManager.loadRules(Arrays.asList(rule, + new DegradeRule("abc").setTimeWindow(20).setCount(20).setSlowRatioThreshold(0.8d))); + assertSame(cb, DegradeRuleManager.getCircuitBreakers(resource).get(0)); + } + @Test public void testIsValidRule() { DegradeRule rule1 = new DegradeRule("abc"); @@ -46,13 +78,19 @@ public class DegradeRuleManagerTest { DegradeRule rule5 = new DegradeRule("Sentinel") .setCount(97) .setGrade(RuleConstant.DEGRADE_GRADE_RT) - .setTimeWindow(15) - .setRtSlowRequestAmount(0); + .setSlowRatioThreshold(15) + .setTimeWindow(15); DegradeRule rule6 = new DegradeRule("Sentinel") .setCount(0.93d) .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) .setTimeWindow(20) .setMinRequestAmount(0); + DegradeRule rule7 = new DegradeRule("Sentinel") + .setCount(100) + .setSlowRatioThreshold(0.8d) + .setTimeWindow(10) + .setStatIntervalMs(0) + .setMinRequestAmount(20); assertFalse(DegradeRuleManager.isValidRule(rule1)); assertFalse(DegradeRuleManager.isValidRule(rule2)); assertFalse(DegradeRuleManager.isValidRule(rule3)); @@ -61,5 +99,6 @@ public class DegradeRuleManagerTest { assertFalse(DegradeRuleManager.isValidRule(rule4)); assertFalse(DegradeRuleManager.isValidRule(rule5)); assertFalse(DegradeRuleManager.isValidRule(rule6)); + assertFalse(DegradeRuleManager.isValidRule(rule7)); } } \ No newline at end of file diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleTest.java new file mode 100644 index 00000000..c879f260 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.csp.sentinel.slots.block.degrade; + +import com.alibaba.csp.sentinel.slots.block.RuleConstant; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @author Eric Zhao + */ +public class DegradeRuleTest { + + @Test + public void testRuleEquals() { + DegradeRule degradeRule1 = new DegradeRule(); + DegradeRule degradeRule2 = new DegradeRule(); + + int minRequestAmount = 20; + double count = 1.0; + int timeWindow = 2; + degradeRule1.setMinRequestAmount(minRequestAmount); + degradeRule1.setCount(count); + degradeRule1.setTimeWindow(timeWindow); + degradeRule1.setGrade(RuleConstant.DEGRADE_GRADE_RT); + + degradeRule2.setMinRequestAmount(minRequestAmount); + degradeRule2.setCount(count); + degradeRule2.setGrade(RuleConstant.DEGRADE_GRADE_RT); + degradeRule2.setTimeWindow(timeWindow); + assertEquals(degradeRule1, degradeRule2); + + degradeRule2.setMinRequestAmount(100); + assertNotEquals(degradeRule1, degradeRule2); + } +} \ No newline at end of file diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeTest.java deleted file mode 100755 index 75668c0b..00000000 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeTest.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.csp.sentinel.slots.block.degrade; - -import com.alibaba.csp.sentinel.EntryType; -import com.alibaba.csp.sentinel.context.Context; -import com.alibaba.csp.sentinel.node.ClusterNode; -import com.alibaba.csp.sentinel.node.DefaultNode; -import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; -import com.alibaba.csp.sentinel.slots.block.RuleConstant; -import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; -import org.junit.Test; - -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * @author jialiang.linjl - */ -public class DegradeTest { - - @Test - public void testAverageRtDegrade() throws InterruptedException { - String key = "test_degrade_average_rt"; - ClusterNode cn = mock(ClusterNode.class); - ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn); - - Context context = mock(Context.class); - DefaultNode node = mock(DefaultNode.class); - when(node.getClusterNode()).thenReturn(cn); - when(cn.avgRt()).thenReturn(2d); - - int rtSlowRequestAmount = 10; - DegradeRule rule = new DegradeRule(); - rule.setCount(1); - rule.setResource(key); - rule.setTimeWindow(2); - rule.setGrade(RuleConstant.DEGRADE_GRADE_RT); - rule.setRtSlowRequestAmount(rtSlowRequestAmount); - - //Will true - for (int i = 0; i < rtSlowRequestAmount - 1; i++) { - assertTrue(rule.passCheck(context, node, 1)); - } - - // The third time will fail. - assertFalse(rule.passCheck(context, node, 1)); - assertFalse(rule.passCheck(context, node, 1)); - - // Restore. - TimeUnit.MILLISECONDS.sleep(2200); - assertTrue(rule.passCheck(context, node, 1)); - } - - @Test - public void testExceptionRatioModeDegrade() throws Throwable { - String key = "test_degrade_exception_ratio"; - ClusterNode cn = mock(ClusterNode.class); - ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn); - - Context context = mock(Context.class); - DefaultNode node = mock(DefaultNode.class); - when(node.getClusterNode()).thenReturn(cn); - - DegradeRule rule = new DegradeRule(); - rule.setCount(0.15); - rule.setResource(key); - rule.setTimeWindow(2); - rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); - rule.setMinRequestAmount(20); - - - // Will true. While totalQps < minRequestAmount - when(cn.totalQps()).thenReturn(8d); - assertTrue(rule.passCheck(context, node, 1)); - - // Will true. - when(cn.totalQps()).thenReturn(21d); - when(cn.successQps()).thenReturn(9d); - when(cn.exceptionQps()).thenReturn(9d); - assertTrue(rule.passCheck(context, node, 1)); - - - // Will true. While totalQps > minRequestAmount and exceptionRation < count - when(cn.totalQps()).thenReturn(100d); - when(cn.successQps()).thenReturn(90d); - when(cn.exceptionQps()).thenReturn(10d); - assertTrue(rule.passCheck(context, node, 1)); - - // Will fail. While totalQps > minRequestAmount and exceptionRation > count - rule.setMinRequestAmount(5); - when(cn.totalQps()).thenReturn(12d); - when(cn.successQps()).thenReturn(8d); - when(cn.exceptionQps()).thenReturn(6d); - assertFalse(rule.passCheck(context, node, 1)); - - // Restore from the degrade timeout. - TimeUnit.MILLISECONDS.sleep(2200); - - // Will pass. - when(cn.totalQps()).thenReturn(106d); - when(cn.successQps()).thenReturn(100d); - assertTrue(rule.passCheck(context, node, 1)); - } - - @Test - public void testExceptionCountModeDegrade() throws Throwable { - String key = "test_degrade_exception_count"; - ClusterNode cn = mock(ClusterNode.class); - when(cn.totalException()).thenReturn(10L); - ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn); - - Context context = mock(Context.class); - DefaultNode node = mock(DefaultNode.class); - when(node.getClusterNode()).thenReturn(cn); - - DegradeRule rule = new DegradeRule(); - rule.setCount(4); - rule.setResource(key); - rule.setTimeWindow(2); - rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT); - - when(cn.totalException()).thenReturn(4L); - - // Will fail. - assertFalse(rule.passCheck(context, node, 1)); - - // Restore from the degrade timeout. - TimeUnit.MILLISECONDS.sleep(2200); - - when(cn.totalException()).thenReturn(0L); - // Will pass. - assertTrue(rule.passCheck(context, node, 1)); - } - - @Test - public void testEquals() { - DegradeRule degradeRule1 = new DegradeRule(); - DegradeRule degradeRule2 = new DegradeRule(); - assertTrue(degradeRule1.equals(degradeRule2)); - - int rtSlowRequestAmount = 10; - int minRequestAmount = 20; - double count = 1.0; - int timeWindow = 2; - degradeRule1.setRtSlowRequestAmount(rtSlowRequestAmount); - degradeRule1.setMinRequestAmount(minRequestAmount); - degradeRule1.setCount(count); - degradeRule1.setTimeWindow(timeWindow); - degradeRule1.setGrade(RuleConstant.DEGRADE_GRADE_RT); - - degradeRule2.setRtSlowRequestAmount(rtSlowRequestAmount); - degradeRule2.setMinRequestAmount(minRequestAmount); - degradeRule2.setCount(count); - degradeRule2.setGrade(RuleConstant.DEGRADE_GRADE_RT); - degradeRule2.setTimeWindow(timeWindow); - assertTrue(degradeRule1.equals(degradeRule2)); - - degradeRule2.setMinRequestAmount(100); - assertFalse(degradeRule1.equals(degradeRule2)); - - - } - -} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/ExceptionCircuitBreakerTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/ExceptionCircuitBreakerTest.java new file mode 100644 index 00000000..22724f27 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/circuitbreaker/ExceptionCircuitBreakerTest.java @@ -0,0 +1,111 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker; + +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker.State; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ExceptionCircuitBreaker.SimpleErrorCounter; +import com.alibaba.csp.sentinel.slots.statistic.base.LeapArray; +import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap; +import com.alibaba.csp.sentinel.test.AbstractTimeBasedTest; + +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author Eric Zhao + */ +public class ExceptionCircuitBreakerTest extends AbstractTimeBasedTest { + + @Test + @SuppressWarnings("unchecked") + public void testStateChangeAndTryAcquire() { + int retryTimeout = 10; + DegradeRule rule = new DegradeRule("abc") + .setCount(0.5d) + .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) + .setStatIntervalMs(20 * 1000) + .setTimeWindow(retryTimeout) + .setMinRequestAmount(10); + LeapArray stat = mock(LeapArray.class); + SimpleErrorCounter counter = new SimpleErrorCounter(); + WindowWrap bucket = new WindowWrap<>(20000, 0, counter); + when(stat.currentWindow()).thenReturn(bucket); + + ExceptionCircuitBreaker cb = new ExceptionCircuitBreaker(rule, stat); + + assertTrue(cb.tryPass()); + assertTrue(cb.tryPass()); + + setCurrentMillis(System.currentTimeMillis()); + cb.fromCloseToOpen(0.52d); + assertEquals(State.OPEN, cb.currentState()); + + assertFalse(cb.tryPass()); + assertFalse(cb.tryPass()); + + // Wait for next retry checkpoint. + sleepSecond(retryTimeout); + sleep(100); + // Try a request to trigger state transformation. + assertTrue(cb.tryPass()); + assertEquals(State.HALF_OPEN, cb.currentState()); + + // Mark this request as error + cb.onRequestComplete(20, new IllegalArgumentException()); + assertEquals(State.OPEN, cb.currentState()); + + // Wait for next retry checkpoint. + sleepSecond(retryTimeout); + sleep(100); + assertTrue(cb.tryPass()); + assertEquals(State.HALF_OPEN, cb.currentState()); + + setCurrentMillis(System.currentTimeMillis()); + // Mark this request as success. + cb.onRequestComplete(20, null); + assertEquals(State.CLOSED, cb.currentState()); + } + + @Test + @SuppressWarnings("unchecked") + public void testRecordErrorOrSuccess() { + DegradeRule rule = new DegradeRule("abc") + .setCount(0.5d) + .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) + .setStatIntervalMs(20 * 1000) + .setTimeWindow(10) + .setMinRequestAmount(10); + LeapArray stat = mock(LeapArray.class); + SimpleErrorCounter counter = new SimpleErrorCounter(); + WindowWrap bucket = new WindowWrap<>(20000, 0, counter); + when(stat.currentWindow()).thenReturn(bucket); + + CircuitBreaker cb = new ExceptionCircuitBreaker(rule, stat); + cb.onRequestComplete(15, null); + + assertEquals(1L, counter.getTotalCount().longValue()); + assertEquals(0L, counter.getErrorCount().longValue()); + + cb.onRequestComplete(15, new IllegalArgumentException()); + assertEquals(2L, counter.getTotalCount().longValue()); + assertEquals(1L, counter.getErrorCount().longValue()); + } +} \ No newline at end of file