An implementation of the Circuit Breaker stability design pattern in the Scala programming language.
At any point in time a circuit-breaker can be in any of the following three states: closed, open or half-open.
During normal operation a circuit-breaker is in the closed state and allows
the execution of the requested operations recording the number of failures
that happen as a result of those operations. A failure is either a thrown
exception for which the configured CircuitConfiguration.isFailure function
returns true or a successful operation execution that takes more than the
configured CircuitConfiguration.maxMethodDuration duration to complete.
When the number of failures that occur within the configured CircuitConfiguration.failureCountTimeout
duration exceeds the CircuitConfiguration.maxFailures then the circuit-breaker
moves to the open state. In the open state, since the probability that
failures will happen is high, the circuit-breaker does not permit the execution
of any requested operation by failing fast. This is achieved by throwing an
OpenCircuitException or by returning a failed Future that contains
OpenCircuitException if the operation is asynchronous.
After the configured CircuitConfiguration.openCircuitTimeout duration has
passed the circuit-breaker moves from the open to the half-open state. In
this state when a request to execute an operation is made, the circuit breaker
allows execution of the operation and if it succeeds it moves to the closed
state, else it moves back to the open state.
To execute operations using a circuit-breaker you need to construct a
CircuitExecutor and pass it the pieces of code you want to execute. This
executor has an associated CircuitBreaker object which holds the state of the
circuit-breaker and is consulted to decide whether to execute the requested
operations or to fail-fast.
To construct a CircuitExecutor you can use the following code:
import com.tzavellas.sse.breaker.{CircuitExecutor, CircuitConfiguration}
import scala.concurrent.duration._
val failFast = new CircuitExecutor(
circuitName="tweets-breaker",
circuitConfig=CircuitConfiguration(
maxFailures = 5,
openCircuitTimeout = 30.seconds,
failureCountTimeout = 1.minute,
maxMethodDuration = 10.seconds)
)The above code will construct a CircuitExecutor with the name "tweets-breaker"
using the specified configuration. The configuration says that if 5 failures
(maxFailures) occur within 1 minute (failureCountTimeout) then the circuit breaker
will move to the open state and move to the half-open state 30 seconds later
(openCircuitTimeout). Also the maximum amount of time an operation might take without
recording it as failure is 10 seconds (maxMethodDuration).
Using the executor with a synchronous operation:
def getTweets(user: String): Seq[Tweet] = ???
val tweets =
try failFast { getTweets("sptz45") }
catch { case e: OpenCircuitException => Seq() }Using the executor with an asynchronous operation (one that returns a Future):
import scala.concurrent.{Future, ExecutionContext}
implicit val executionContext = ExecutionContext.fromExecutor(???)
def getTweetsAsync(user: String): Future[Seq[Tweet]] = ???
val tweets = failFast(getTweetsAsync("sptz45"))
.recover { case _: OpenCircuitException => Seq() }Using the executor by launching a synchronous operation in an ExecutionContext and returning a Future:
import scala.concurrent.{Future, ExecutionContext}
implicit val executionContext = ExecutionContext.fromExecutor(???)
def getTweets(user: String): Seq[Tweet] = ???
val tweets = failFast.async(getTweets("sptz45"))
.recover { case _: OpenCircuitException => Seq() }For Scala 2.10.x use:
<dependency>
<groupId>com.tzavellas</groupId>
<artifactId>sse-breaker</artifactId>
<version>0.8.0</version>
</dependency>Licensed under the Apache License, Version 2.0. See the LICENSE and NOTICE files for more information.
