From 7e698a90dd7664e119ec0b082883ea48dc12ae2e Mon Sep 17 00:00:00 2001 From: Aditya Pal Date: Tue, 15 Oct 2019 00:02:19 +0530 Subject: [PATCH] Fix for issue #413: Circuit Breaker Pattern (#986) * Fix Issue #413: Circuit-Breaker Pattern * Fix Image Links * Remove Javadoc plugin to ensure correct build * Implementing code review feedback * Sync README with actual code --- circuit-breaker/README.md | 188 ++++++++++++++++++ circuit-breaker/etc/ServiceDiagram.PNG | Bin 0 -> 24371 bytes circuit-breaker/etc/StateDiagram.PNG | Bin 0 -> 19396 bytes circuit-breaker/pom.xml | 40 ++++ .../java/com/iluwatar/circuitbreaker/App.java | 86 ++++++++ .../circuitbreaker/CircuitBreaker.java | 128 ++++++++++++ .../circuitbreaker/DelayedService.java | 61 ++++++ .../circuitbreaker/MonitoringService.java | 50 +++++ .../com/iluwatar/circuitbreaker/State.java | 33 +++ .../circuitbreaker/CircuitBreakerTest.java | 80 ++++++++ .../circuitbreaker/DelayedServiceTest.java | 42 ++++ .../circuitbreaker/MonitoringServiceTest.java | 63 ++++++ pom.xml | 1 + 13 files changed, 772 insertions(+) create mode 100644 circuit-breaker/README.md create mode 100644 circuit-breaker/etc/ServiceDiagram.PNG create mode 100644 circuit-breaker/etc/StateDiagram.PNG create mode 100644 circuit-breaker/pom.xml create mode 100644 circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java create mode 100644 circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java create mode 100644 circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedService.java create mode 100644 circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java create mode 100644 circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/State.java create mode 100644 circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/CircuitBreakerTest.java create mode 100644 circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DelayedServiceTest.java create mode 100644 circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java diff --git a/circuit-breaker/README.md b/circuit-breaker/README.md new file mode 100644 index 000000000..db75ca58c --- /dev/null +++ b/circuit-breaker/README.md @@ -0,0 +1,188 @@ +--- +layout: pattern +title: CircuitBreaker +folder: circuit-breaker +permalink: /patterns/circuit-breaker/ +categories: Other +tags: + - Java + - Performance + - Difficulty-Intermediate +--- + +## Intent + +Handle costly remote *procedure/service* calls in such a way that the failure of a **single** service/component cannot bring the whole application down, and we can reconnect to the service as soon as possible. + +## Explanation + +Real world example + +> Imagine a Web App that has both local (example: files and images) and remote (example: database entries) to serve. The database might not be responding due to a variety of reasons, so if the application keeps trying to read from the database using multiple threads/processes, soon all of them will hang and our entire web application will crash. We should be able to detect this situation and show the user an appropriate message so that he/she can explore other parts of the app unaffected by the database failure without any problem. + +In plain words + +> Allows us to save resources when we know a remote service failed. Useful when all parts of our application are highly decoupled from each other, and failure of one component doesn't mean the other parts will stop working. + +Wikipedia says + +> **Circuit breaker** is a design pattern used in modern software development. It is used to detect failures and encapsulates the logic of preventing a failure from constantly recurring, during maintenance, temporary external system failure or unexpected system difficulties. + +So, how does this all come together? + +## Programmatic Example +With the above example in mind we will imitate the functionality in a simple manner. We have two services: A *monitoring service* which will mimic the web app and will make both **local** and **remote** calls. + +The service architecture is as follows: + +![alt text](./etc/ServiceDiagram.PNG "Service Diagram") + +In terms of code, the End user application is: + +```java +public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + public static void main(String[] args) { + var obj = new MonitoringService(); + var circuitBreaker = new CircuitBreaker(3000, 1, 2000 * 1000 * 1000); + var serverStartTime = System.nanoTime(); + while (true) { + LOGGER.info(obj.localResourceResponse()); + LOGGER.info(obj.remoteResourceResponse(circuitBreaker, serverStartTime)); + LOGGER.info(circuitBreaker.getState()); + try { + Thread.sleep(5 * 1000); + } catch (InterruptedException e) { + LOGGER.error(e.getMessage()); + } + } + } +} +``` + +The monitoring service is: + +``` java +public class MonitoringService { + + public String localResourceResponse() { + return "Local Service is working"; + } + + public String remoteResourceResponse(CircuitBreaker circuitBreaker, long serverStartTime) { + try { + return circuitBreaker.call("delayedService", serverStartTime); + } catch (Exception e) { + return e.getMessage(); + } + } +} +``` +As it can be seen, it does the call to get local resources directly, but it wraps the call to remote (costly) service in a circuit breaker object, which prevents faults as follows: + +```java +public class CircuitBreaker { + private final long timeout; + private final long retryTimePeriod; + long lastFailureTime; + int failureCount; + private final int failureThreshold; + private State state; + private final long futureTime = 1000 * 1000 * 1000 * 1000; + + CircuitBreaker(long timeout, int failureThreshold, long retryTimePeriod) { + this.state = State.CLOSED; + this.failureThreshold = failureThreshold; + this.timeout = timeout; + this.retryTimePeriod = retryTimePeriod; + this.lastFailureTime = System.nanoTime() + futureTime; + this.failureCount = 0; + } + + private void reset() { + this.failureCount = 0; + this.lastFailureTime = System.nanoTime() + futureTime; + this.state = State.CLOSED; + } + + private void recordFailure() { + failureCount = failureCount + 1; + this.lastFailureTime = System.nanoTime(); + } + + protected void setState() { + if (failureCount > failureThreshold) { + if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { + state = State.HALF_OPEN; + } else { + state = State.OPEN; + } + } else { + state = State.CLOSED; + } + } + + public String getState() { + return state.name(); + } + + public void setStateForBypass(State state) { + this.state = state; + } + + public String call(String serviceToCall, long serverStartTime) throws Exception { + setState(); + if (state == State.OPEN) { + return "This is stale response from API"; + } else { + if (serviceToCall.equals("delayedService")) { + var delayedService = new DelayedService(20); + var response = delayedService.response(serverStartTime); + if (response.split(" ")[3].equals("working")) { + reset(); + return response; + } else { + recordFailure(); + throw new Exception("Remote service not responding"); + } + } else { + throw new Exception("Unknown Service Name"); + } + } + } +} +``` + +How does the above pattern prevent failures? Let's understand via this finite state machine implemented by it. + +![alt text](./etc/StateDiagram.PNG "State Diagram") + +- We initialize the Circuit Breaker object with certain parameters: **timeout**, **failureThreshold** and **retryTimePeriod** which help determine how resilient the API is. +- Initially, we are in the **closed** state and the remote call to API happens. +- Every time the call succeeds, we reset the state to as it was in the beginning. +- If the number of failures cross a certain threshold, we move to the **open** state, which acts just like an open circuit and prevents remote service calls from being made, thus saving resources. (Here, we return the response called ```stale response from API```) +- Once we exceed the retry timeout period, we move to the **half-open** state and make another call to the remote service again to check if the service is working so that we can serve fresh content. A *failure* sets it back to **open** state and another attempt is made after retry timeout period, while a *success* sets it to **closed** state so that everything starts working normally again. + + +## Applicability +Use the Circuit Breaker pattern when + +- Building a fault-tolerant application where failure of some services shouldn't bring the entire application down. +- Building an continuously incremental/continuous delivery application, as some of it's components can be upgraded without shutting it down entirely. + +## Related Patterns + +- [Retry Pattern](https://github.com/iluwatar/java-design-patterns/tree/master/retry) + +## Real world examples +* [Spring Circuit Breaker module](https://spring.io/guides/gs/circuit-breaker) +* [Netflix Hystrix API](https://github.com/Netflix/Hystrix) + +## Credits + +* [Understanding Circuit Breaker Pattern](https://itnext.io/understand-circuitbreaker-design-pattern-with-simple-practical-example-92a752615b42) +* [Martin Fowler on Circuit Breaker](https://martinfowler.com/bliki/CircuitBreaker.html) +* [Fault tolerance in a high volume, distributed system](https://medium.com/netflix-techblog/fault-tolerance-in-a-high-volume-distributed-system-91ab4faae74a) +* [Microsoft docs](https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) diff --git a/circuit-breaker/etc/ServiceDiagram.PNG b/circuit-breaker/etc/ServiceDiagram.PNG new file mode 100644 index 0000000000000000000000000000000000000000..661f1a1058181b689b0724418e598f7b5b245c31 GIT binary patch literal 24371 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 literal 0 HcmV?d00001 diff --git a/circuit-breaker/etc/StateDiagram.PNG b/circuit-breaker/etc/StateDiagram.PNG new file mode 100644 index 0000000000000000000000000000000000000000..38485526d34288bac6d2b0435437c13503f3c567 GIT binary patch literal 19396 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 literal 0 HcmV?d00001 diff --git a/circuit-breaker/pom.xml b/circuit-breaker/pom.xml new file mode 100644 index 000000000..b6587b061 --- /dev/null +++ b/circuit-breaker/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.22.0-SNAPSHOT + + circuit-breaker + + + org.junit.jupiter + junit-jupiter-engine + test + + + diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java new file mode 100644 index 000000000..d1d361b16 --- /dev/null +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.circuitbreaker; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

+ * The intention of the Circuit Builder pattern is to handle remote failures + * robustly, which is to mean that if a service is dependant on n number of + * other services, and m of them fail, we should be able to recover from that + * failure by ensuring that the user can still use the services that are actually + * functional, and resources are not tied up by uselessly by the services which + * are not working. However, we should also be able to detect when any of the m + * failing services become operational again, so that we can use it + *

+ *

+ * In this example, the circuit breaker pattern is demonstrated by using two services: + * {@link MonitoringService} and {@link DelayedService}. The monitoring service + * is responsible for calling two services: a local service and a remote service {@link DelayedService} + * , and by using the circuit breaker construction we ensure that if the call to + * remote service is going to fail, we are going to save our resources and not make the + * function call at all, by wrapping our call to the remote service in the circuit + * breaker object. + *

+ *

+ * This works as follows: The {@link CircuitBreaker} object can be in one of three + * states: Open, Closed and Half-Open, which represents the real + * world circuits. If the state is closed (initial), we assume everything is alright + * and perform the function call. However, every time the call fails, we note it + * and once it crosses a threshold, we set the state to Open, preventing any further + * calls to the remote server. Then, after a certain retry period (during which we + * expect thee service to recover), we make another call to the remote server and + * this state is called the Half-Open state, where it stays till the service is down, + * and once it recovers, it goes back to the closed state and the cycle continues. + *

+ */ +public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + //Create an object of monitoring service which makes both local and remote calls + var obj = new MonitoringService(); + //Set the circuit Breaker parameters + var circuitBreaker = new CircuitBreaker(3000, 1, 2000 * 1000 * 1000); + var serverStartTime = System.nanoTime(); + while (true) { + LOGGER.info(obj.localResourceResponse()); + LOGGER.info(obj.remoteResourceResponse(circuitBreaker, serverStartTime)); + LOGGER.info(circuitBreaker.getState()); + try { + Thread.sleep(5 * 1000); + } catch (InterruptedException e) { + LOGGER.error(e.getMessage()); + } + } + } +} diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java new file mode 100644 index 000000000..ad014f9fd --- /dev/null +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java @@ -0,0 +1,128 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.circuitbreaker; + +/** + * The circuit breaker class with all configurations + */ +public class CircuitBreaker { + private final long timeout; + private final long retryTimePeriod; + long lastFailureTime; + int failureCount; + private final int failureThreshold; + private State state; + private final long futureTime = 1000 * 1000 * 1000 * 1000; + + /** + * Constructor to create an instance of Circuit Breaker + * @param timeout Timeout for the API request. Not necessary for this simple example + * @param failureThreshold Number of failures we receive from the depended service before changing state to 'OPEN' + * @param retryTimePeriod Time period after which a new request is made to remote service for status check. + */ + CircuitBreaker(long timeout, int failureThreshold, long retryTimePeriod) { + // We start in a closed state hoping that everything is fine + this.state = State.CLOSED; + this.failureThreshold = failureThreshold; + // Timeout for the API request. Used to break the calls made to remote resource if it exceeds the limit + this.timeout = timeout; + this.retryTimePeriod = retryTimePeriod; + //An absurd amount of time in future which basically indicates the last failure never happened + this.lastFailureTime = System.nanoTime() + futureTime; + this.failureCount = 0; + } + + //Reset everything to defaults + private void reset() { + this.failureCount = 0; + this.lastFailureTime = System.nanoTime() + futureTime; + this.state = State.CLOSED; + } + + private void recordFailure() { + failureCount = failureCount + 1; + this.lastFailureTime = System.nanoTime(); + } + + protected void setState() { + if (failureCount > failureThreshold) { //Then something is wrong with remote service + if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { + //We have waited long enough and should try checking if service is up + state = State.HALF_OPEN; + } else { + //Service would still probably be down + state = State.OPEN; + } + } else { + //Everything is working fine + state = State.CLOSED; + } + } + + public String getState() { + return state.name(); + } + + /** + * Break the circuit beforehand if it is known service is down + * Or connect the circuit manually if service comes online before expected + * @param state State at which circuit is in + */ + public void setStateForBypass(State state) { + this.state = state; + } + + /** + * @param serviceToCall The name of the service in String. Can be changed to data URLs in case of web applications + * @param serverStartTime Time at which actual server was started which makes calls to this service + * @return Value from the remote resource, stale response or a custom exception + */ + public String call(String serviceToCall, long serverStartTime) throws Exception { + setState(); + if (state == State.OPEN) { + // return cached response if no the circuit is in OPEN state + return "This is stale response from API"; + } else { + // Make the API request if the circuit is not OPEN + if (serviceToCall.equals("delayedService")) { + var delayedService = new DelayedService(20); + var response = delayedService.response(serverStartTime); + //In a real application, this would be run in a thread and the timeout + //parameter of the circuit breaker would be utilized to know if service + //is working. Here, we simulate that based on server response itself + if (response.split(" ")[3].equals("working")) { + // Yay!! the API responded fine. Let's reset everything. + reset(); + return response; + } else { + // Uh-oh!! the call still failed. Let's update that in our records. + recordFailure(); + throw new Exception("Remote service not responding"); + } + } else { + throw new Exception("Unknown Service Name"); + } + } + } +} \ No newline at end of file diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedService.java new file mode 100644 index 000000000..33217b8e7 --- /dev/null +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedService.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.circuitbreaker; + +/** +* This simulates the remote service +* It responds only after a certain timeout period (default set to 20 seconds) +*/ +public class DelayedService { + private final int delay; + + /** + * Constructor to create an instance of DelayedService, which is down for first few seconds + * @param delay the delay after which service would behave properly, in seconds + */ + public DelayedService(int delay) { + this.delay = delay; + } + + public DelayedService() { + this.delay = 60; + } + + /** + * @param serverStartTime Time at which actual server was started which makes calls to this service + * @return The state of the service + */ + public String response(long serverStartTime) { + var currentTime = System.nanoTime(); + //Since currentTime and serverStartTime are both in nanoseconds, we convert it to + //seconds by diving by 10e9 and ensure floating point division by multiplying it + //with 1.0 first. We then check if it is greater or less than specified delay and then + //send the reply + if ((currentTime - serverStartTime) * 1.0 / (1000 * 1000 * 1000) < delay) { + //Can use Thread.sleep() here to block and simulate a hung server + return "Delayed service is down"; + } else { + return "Delayed service is working"; + } + } +} diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java new file mode 100644 index 000000000..e301ff3ca --- /dev/null +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.circuitbreaker; + +/** + * The service class which makes local and remote calls + * Uses {@link CircuitBreaker} object to ensure remote calls don't use up resources + */ +public class MonitoringService { + + //Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic + public String localResourceResponse() { + return "Local Service is working"; + } + + /** + * Try to get result from remote server + * @param circuitBreaker The circuitBreaker object with all parameters + * @param serverStartTime Time at which actual server was started which makes calls to this service + * @return result from the remote response or exception raised by it. + */ + public String remoteResourceResponse(CircuitBreaker circuitBreaker, long serverStartTime) { + try { + return circuitBreaker.call("delayedService", serverStartTime); + } catch (Exception e) { + return e.getMessage(); + } + } +} \ No newline at end of file diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/State.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/State.java new file mode 100644 index 000000000..87982153c --- /dev/null +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/State.java @@ -0,0 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.circuitbreaker; + +/** + * Enumeration for states the circuit breaker could be in + */ +public enum State { + CLOSED, + OPEN, + HALF_OPEN +} \ No newline at end of file diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/CircuitBreakerTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/CircuitBreakerTest.java new file mode 100644 index 000000000..5780e9355 --- /dev/null +++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/CircuitBreakerTest.java @@ -0,0 +1,80 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.circuitbreaker; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + +/** + * + * Circuit Breaker test + * + */ +public class CircuitBreakerTest { + + //long timeout, int failureThreshold, long retryTimePeriod + @Test + public void testSetState() { + var circuitBreaker = new CircuitBreaker(1,1,100); + //Right now, failureCountfailureThreshold, and lastFailureTime is nearly equal to current time, + //state should be half-open + assertEquals(circuitBreaker.getState(), "HALF_OPEN"); + //Since failureCount>failureThreshold, and lastFailureTime is much lesser current time, + //state should be open + circuitBreaker.lastFailureTime = System.nanoTime() - 1000 * 1000 * 1000 * 1000; + circuitBreaker.setState(); + assertEquals(circuitBreaker.getState(), "OPEN"); + //Now set it back again to closed to test idempotency + circuitBreaker.failureCount = 0; + circuitBreaker.setState(); + assertEquals(circuitBreaker.getState(), "CLOSED"); + } + + @Test + public void testSetStateForBypass() { + var circuitBreaker = new CircuitBreaker(1,1,100); + //Right now, failureCountbytecode leader-election data-locality + circuit-breaker