Installation of circuit breaker

If you use maven, add maven dependency for jcircuitbreaker-core to your project:

  <dependency>
      <groupId>net.secodo.jcircuitbreaker</groupId>
      <artifactId>jcircuitbreaker-core</artifactId>
      <version>1.2.0</version>
  </dependency>

If you use another build system like (Gradle, sbt, etc.) you should include the dependency as defined on dependency-info page. Otherwise follow the instruction on download page to obtain the artifact and place it on your application classpath.

The library uses NO external dependencies, so your project will not get any transitive libraries deployed together with JCircuitBreaker java library.

Circuit Breaker contract

Main goal of a circuit breaker is to dynamically decide if a specified resource / java method should be executed or not. Consideration about motivation for usage of circuit breaker is made on Introduction page.

A java method that is protected by circuit breaker is called target-method or Task (written with capital letter).

Circuit breaker mechanism consists of four components:

  • CircuitBreaker - controls the logic, connects all components together
  • Task - represents a target-method - java method which should be protected by Circuit Breaker. It is encapsulated in Task interface for method returning values. It is encapsulated in VoidTask interface for void methods which does not return value
  • BreakStrategy - decides whether given Task should be executed or break should occur (in later case the Task is NOT executed due to conditions defined by the strategy)
  • BreakHandler - defines what should happen in case break occurred (target-method was not invoked). Break handler can for example retry the attempt, return some default value or provide some custom behaviour.

All above components are connected via CircuitBreaker interface. This interface defines “execute” method which takes Task, break strategy and break handler as parameters. Optionally this method can take custom object containing data which will be available to break strategy and break handler (through instance of ExecutionContext ).

public interface CircuitBreaker<R> {

 default R execute(Task<R> task, BreakStrategy<R> breakStrategy, BreakHandler<R> breakHandler)
             throws TaskExecutionException, BreakHandlerException, CircuitBreakerException {
    return execute(task, breakStrategy, breakHandler, null);
  }

  <U> R execute(Task<R> task, BreakStrategy<R> breakStrategy, BreakHandler<R> breakHandler,
                U userData) throws TaskExecutionException, BreakHandlerException, CircuitBreakerException;

}

Both above methods throws three types of exception. TaskExecutionException is a checked exception that must be handled by caller of execute method. This exception signifies that target-method was executed and resulted in exception. TaskExecutionException contains reference to the original exception thrown by target-method. BreakHandlerException is a runtime exception which signifies that break handler was executed (instead of target-method) but was not able to provide a fallback value. CircuitBreakerException is a runtime exception which signifies some problem with running circuit breaker. You should never see it, unless some really unexpected situation happened which prevented circuit breaker from doing it’s job.

The generic parameter R signifies the return value of the target-method, This allows the execute method to return exactly the same Object which is returned by target-method.

Usage

There are currently two ready to use implementations of this mechanism. The main implementation is DefaultCircuitBreaker which basically exposes the methods of CircuitBreaker interface to the client. This implementation requires break strategy and break handler to be provided each time circuit breaker is used.

Executing methods returning values

To examine the usage, let’s first define some target-method which we would like to protect. This will be a service method located in SomeServiceClass class. This class provides a method with potentially long execution time. We would like to avoid situation when too many threads will invoke this method as this may result in breaching stability of our application.

public class SomeServiceClass {
  public Long somePossiblyLongRunningMethod(int callId) {
    // some heavy and long running operation here
    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {}
    return (long) 1;
  }
}  

Instance of SomeServiceClass is called by a MyCaller class via runService method:

public class MyCaller {

  private final SomeServiceClass myService;

  public MyCaller() {
    this.myService = new SomeServiceClass();
  }

  public Long runService(int serviceParam)  {
     return myService.somePossiblyLongRunningMethod(serviceParam);
  }

}

Now let’s imagine that runService method can be simultaneously accessed by many threads. Because somePossiblyLongRunningMethod method will run long it may consume a lot of resources. Therefore we would like to limit the number of concurrent threads that can access somePossiblyLongRunningMethod to protect our application stability. For this we define the circuit breaker which limits access only to 3 concurrent threads at maximum. New version of MyCaller class (protected by circuit breaker) is following:

public class MyCaller {

  private final SomeServiceClass myService;
  private final CircuitBreaker<Long> circuitBreaker;
  private final BreakHandler<Long> breakHandler;
  private final BreakStrategy<Long> breakStrategy;

  public MyCaller() {
    this.myService = new SomeServiceClass();

    // prepare the circuit breaker
    this.circuitBreaker = new DefaultCircuitBreaker<>();
    this.breakStrategy = new LimitedConcurrentExecutionsStrategy<>(3l); // we define a break strategy that allows only 3 executions in parallel
    this.breakHandler = new ReturnStaticValueHandler<>(-1l); // we define a break handler which returns -1 in case circuit breaker prevents execution of myService.somePossiblyLongRunningMethod 
  }

  public Long runService(int serviceParam)  {
    try {
      return circuitBreaker.execute(() -> myService.somePossiblyLongRunningMethod(serviceParam), breakStrategy, breakHandler);
    } catch (TaskExecutionException e) { 
      // TaskExecutionException can be thrown by circuit breaker "only and only" if myService.somePossiblyLongRunningMethod thrown exception
      System.out.println("Calling somePossiblyLongRunningMethod resulted in exception: " + e.getTaskException());
      throw new RuntimeException(e.getTaskException().getMessage(), e.getTaskException()); // getTaskExcepition() returns possible exception thrown by myService.somePossiblyLongRunningMethod(serviceParam) 
    }
  }
}

Now when many threads will run MyCaller.runService(), circuit breaker will allow each execution only in case there are less than 3 executions in progress. This is defined by the break strategy. In case a break occurred break handler takes control and returns -1 instead of the real value.

NOTE: Above example (together with live simulation) can be found within test sources folder of the library in directory examples/example1.

Of course the behaviour od both break strategy and break handler can be changed. There are some ready to use break strategies and break handlers available. For the list see break strategies api docs and break handlers api docs. More implementations are targeted for the next releases of the JCircuitBreaker. You can always provide your own implementations (see “Custom implementations” section below) simply by implementing BreakHandler interface or BreakStrategy interface

Reusable circuit breaker

There is also an implementation of CircuitBreaker interface which stores the break handler and break strategy as fields, so that there is no need to supply them each time circuit breaker is executed. This class is called ReusableCircuitBreaker and can be built using fluent interface by calling builder() method or builderP(). Both methods do exactly the same - return instance of ReusableCircuitBreakerBuilder which is used to construct instances of ReusableCircuitBreaker. The difference is that second method is parametrized to avoid unchecked casts warning (however some people prefer to use non-parametrized method builder() for easiness).

Executing Tasks with no return value (void)

Execution of void methods which does not return value is similar to methods which returns values. The slight difference is that the target-method should be enclosed in VoidTask interface instead of Task.

Let’s examine example of sendPing() method which does not return value:

public class PingService {
  private Random random = new Random();

  public void sendPing(int pingId) throws IOException {
    try {
      Thread.sleep(500 + random.nextInt(1000));
    } catch (InterruptedException e) {
    }
    System.out.println("ping made #" + pingId);
  }
}

public class MyCaller {
  private final PingService pingService;

  public MyCaller() {
    this.pingService = new PingService();
  }

  public void ping(int pingId) throws IOException {
    pingService.sendPing(pingId);
  }
}

MyCaller class calls pingService to send ping to remote resource. It does not make sense to send a ping if there is another in progress. When the remote service does not respond shortly and we keep calling sendPing (as in above example) we might run into a situation when we send more pings than the service is able to receive. To solve this we create a circuit breaker which will limit number of pings. For this we modify MyCaller class as follows:

public class MyCallerWithCircuitBreaker {
  private final PingService pingService;
  private final CircuitBreaker<Void> circuitBreaker;
  private final BreakHandler<Void> breakHandler;
  private final BreakStrategy<Void> breakStrategy;

  public MyCallerWithCircuitBreaker() {
    this.pingService = new PingService();

    // prepare the circuit breaker
    this.circuitBreaker = new DefaultCircuitBreaker<>();
    this.breakStrategy = new SingleExecutionAllowedStrategy<>(); // one execution a time allowed
    this.breakHandler = new NoActionHandler<>(); // we don't care about "pings" which were skipped
  }

  public void ping(int pingId) throws IOException {
    try {
      // NOTE: notice cast to VoidTask below
      circuitBreaker.execute((VoidTask) () -> pingService.sendPing(pingId), breakStrategy, breakHandler); 
    } catch (TaskExecutionException e) {
      System.out.println("Calling ping resulted in exception: " + e.getTaskException());
      throw new IOException(e.getTaskException().getMessage(), e.getTaskException());
    }
  }
}

In above example Void class is used as generic parameter for CircuitBreaker, BreakHandler and BreakStrategy. This denotes that the method which will be executed by the circuit breaker will be “void” method (method which does not return value). Additionally encapsulated target-method call is casted to VoidTask (when “execute” method of circuit breaker is called). This is required when CircuitBreaker is used to protect void methods.

NOTE: Example similar to one presented above (together with live simulation) can be found within test sources folder of the library in directory examples/example2. That example uses a little different strategy which allows execution basing on average execution time of other pings currently in progress.

Using built-in dsl to create chain of strategies

TODO: document how to use SimpleStrategyDsl

Custom implementations

TODO: document how to create custom break strategies and break handlers TODO: document how to use more than one break handler