# Vert.x Circuit Breaker

Vert.x Circuit Breaker is an implementation of the circuit breaker pattern for Vert.x. It keeps track of the number of recent failures and prevents further executions when a threshold is reached. Optionally, a fallback is executed.

Supported failures are:

- failures reported by your code in a `Future`

- exception thrown by your code

- uncompleted futures (timeout)

Operations guarded by a circuit breaker are intended to be non-blocking and asynchronous in order to benefit from the Vert.x execution model.

## Using Vert.x Circuit Breaker

To use Vert.x Circuit Breaker, add the following dependency to the dependencies section of your build descriptor:

- Maven (in your `pom.xml`):

    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-circuit-breaker</artifactId>
      <version>5.0.0.CR1</version>
    </dependency>

- Gradle (in your `build.gradle` file):

    compile 'io.vertx:vertx-circuit-breaker:5.0.0.CR1'

## Using the circuit breaker

To use the circuit breaker you need to:

1. Create a circuit breaker, with the configuration you want (timeout, failure threshold)

2. Execute some code using the circuit breaker

Important: Don’t recreate a circuit breaker on every call. A circuit breaker is a stateful entity. It is recommended to store the circuit breaker instance in a field.

Here is an example:

    CircuitBreaker breaker = CircuitBreaker.create("my-circuit-breaker", vertx,
      new CircuitBreakerOptions()
        .setMaxFailures(5) // number of failures before opening the circuit breaker
        .setTimeout(2000) // considered a failure if the operation does not succeed in time
        .setFallbackOnFailure(true) // call the fallback on failure
        .setResetTimeout(10000) // time spent in open state before attempting to retry
    );

    // ---
    // Store the circuit breaker in a field and access it as follows
    // ---

    breaker.execute(promise -> {
      // some code executing with the circuit breaker
      // the code reports failures or success on the given promise
      // if this promise is marked as failed, the circuit breaker
      // increases the number of failures
    }).onComplete(ar -> {
      // Get the operation result.
    });

The executed block receives a `Promise` object as parameter, to denote the success or failure of the operation as well as the result. In the following example, the result is the output of a REST endpoint invocation:

    CircuitBreaker breaker = CircuitBreaker.create("my-circuit-breaker", vertx,
      new CircuitBreakerOptions().setMaxFailures(5).setTimeout(2000)
    );

    // ---
    // Store the circuit breaker in a field and access it as follows
    // ---

    breaker.<String>execute(promise -> {
      vertx.createHttpClient().request(HttpMethod.GET, 8080, "localhost", "/")
        .compose(req -> req
          .send()
          .compose(resp -> {
            if (resp.statusCode() != 200) {
              return Future.failedFuture("HTTP error");
            } else {
              return resp.body().map(Buffer::toString);
            }
          }))
        .onComplete(promise);
    }).onComplete(ar -> {
      // Do something with the result
    });

The result of the operation is provided using the:

- returned `Future` when calling `execute` methods

- provided `Promise` when calling the `executeAndReport` methods

Optionally, you can provide a fallback which is executed when the circuit breaker is open:

    CircuitBreaker breaker = CircuitBreaker.create("my-circuit-breaker", vertx,
      new CircuitBreakerOptions().setMaxFailures(5).setTimeout(2000)
    );

    // ---
    // Store the circuit breaker in a field and access it as follows
    // ---

    breaker.executeWithFallback(promise -> {
      vertx.createHttpClient().request(HttpMethod.GET, 8080, "localhost", "/")
        .compose(req -> req
          .send()
          .compose(resp -> {
            if (resp.statusCode() != 200) {
              return Future.failedFuture("HTTP error");
            } else {
              return resp.body().map(Buffer::toString);
            }
          }))
        .onComplete(promise);
    }, v -> {
      // Executed when the circuit breaker is open
      return "Hello";
    }).onComplete(ar -> {
      // Do something with the result
    });

The fallback is called when the circuit breaker is open, or when `isFallbackOnFailure` is enabled. When fallback is set, the overall result is obtained by calling the fallback function. The fallback function takes as parameter a `Throwable` object and returns an object of the expected type.

The fallback can also be set on the `CircuitBreaker` object directly:

    CircuitBreaker breaker = CircuitBreaker.create("my-circuit-breaker", vertx,
      new CircuitBreakerOptions().setMaxFailures(5).setTimeout(2000)
    ).fallback(v -> {
      // Executed when the circuit breaker is open.
      return "hello";
    });

    breaker.<String>execute(promise -> {
      vertx.createHttpClient().request(HttpMethod.GET, 8080, "localhost", "/")
        .compose(req -> req
          .send()
          .compose(resp -> {
            if (resp.statusCode() != 200) {
              return Future.failedFuture("HTTP error");
            } else {
              return resp.body().map(Buffer::toString);
            }
          }))
        .onComplete(promise);
    });

### Reported exceptions

The fallback receives:

- `OpenCircuitException` when the circuit breaker is open

- `TimeoutException` when the operation timed out

## Retries

You can also specify how often the circuit breaker should execute your code before failing with `setMaxRetries`. If you set this to something higher than 0, your code gets executed several times before finally failing in the last execution. If the code succeeds in one of the retries, your handler gets notified and no more retries occur. Retries are only supported when the circuit breaker is closed.

| If you set `maxRetries` to 2, your operation may be called 3 times: the initial attempt and 2 retries.

By default, the delay between retries is set to 0, which means that retries will be executed one after another immediately. This, however, will result in increased load on the called service and may delay its recovery. In order to mitigate this problem, it is recommended to execute retries with a delay.

The `retryPolicy` method can be used to specify a retry policy. A retry policy is a function which receives the operation failure and retry count as arguments and returns a delay in milliseconds before retry should be executed.

It allows to implement complex policies, e.g. using the value of the `Retry-After` header sent by an unavailable service. Some common policies are provided out of the box: `RetryPolicy.constantDelay`, `RetryPolicy.linearDelay` and `RetryPolicy.exponentialDelayWithJitter`

Below is an example of exponential delay with jitter:

    CircuitBreaker breaker = CircuitBreaker.create("my-circuit-breaker", vertx,
      new CircuitBreakerOptions().setMaxFailures(5).setMaxRetries(5).setTimeout(2000)
    ).retryPolicy(RetryPolicy.exponentialDelayWithJitter(50, 500));

    breaker.<String>execute(promise -> {
      vertx.createHttpClient().request(HttpMethod.GET, 8080, "localhost", "/")
        .compose(req -> req
          .send()
          .compose(resp -> {
            if (resp.statusCode() != 200) {
              return Future.failedFuture("HTTP error");
            } else {
              return resp.body().map(Buffer::toString);
            }
          }))
        .onComplete(promise);
    });

## Failure Policy

By default, the failure policy of a circuit breaker is to report a failure if the command doesn’t complete successfully. Alternatively, you may configure the failure policy of the circuit breaker with `failurePolicy`. This will let you specify the criteria in which an `AsyncResult` is treated as a failure by the circuit breaker. If you decide to override the failure policy, just be aware that it could allow failed results in the future provided in functions like `executeAndReport`.

Below is an example of using a custom defined failure policy.

    CircuitBreaker breaker = CircuitBreaker.create("my-circuit-breaker", vertx);
    breaker.<HttpClientResponse>failurePolicy(ar -> {
      // A failure will be either a failed operation or a response with a status code other than 200
      if (ar.failed()) {
        return true;
      }
      HttpClientResponse resp = ar.result();
      return resp.statusCode() != 200;
    });

    Future<HttpClientResponse> future = breaker.execute(promise -> {
      vertx.createHttpClient()
        .request(HttpMethod.GET, 8080, "localhost", "/")
        .compose(request -> request.send()
          // Complete when the body is fully received
          .compose(response -> response.body().map(response)))
        .onComplete(promise);
    });

## Callbacks

You can also configure callbacks invoked when the circuit breaker is opened or closed:

    CircuitBreaker breaker = CircuitBreaker.create("my-circuit-breaker", vertx,
      new CircuitBreakerOptions().setMaxFailures(5).setTimeout(2000)
    ).openHandler(v -> {
      System.out.println("Circuit breaker opened");
    }).closeHandler(v -> {
      System.out.println("Circuit breaker closed");
    });

    breaker.<String>execute(promise -> {
      vertx.createHttpClient().request(HttpMethod.GET, 8080, "localhost", "/")
        .compose(req -> req
          .send()
          .compose(resp -> {
            if (resp.statusCode() != 200) {
              return Future.failedFuture("HTTP error");
            } else {
              return resp.body().map(Buffer::toString);
            }
          }))
        .onComplete(promise);
    });

You can also be notified when the circuit breaker moves to the half-open state, in an attempt to reset. You can register such a callback with `halfOpenHandler`.

## Event bus notification

Every time the circuit breaker state changes, an event can be published on the event bus.

To enable this feature, set the `notification address` to a value that is not `null`:

    options.setNotificationAddress(CircuitBreakerOptions.DEFAULT_NOTIFICATION_ADDRESS);

The event contains circuit breaker metrics. Computing these metrics requires the following dependency to be added the dependencies section of your build descriptor:

- Maven (in your `pom.xml`):

    <dependency>
      <groupId>org.hdrhistogram</groupId>
      <artifactId>HdrHistogram</artifactId>
      <version>2.1.12</version>
    </dependency>

- Gradle (in your `build.gradle` file):

    compile 'org.hdrhistogram:HdrHistogram:2.1.12'

| When enabled, notifications are delivered only to local consumers by default. If the notification must be sent to all consumers in a cluster, you can change this behavior with `setNotificationLocalOnly`.

Each event contains a Json Object with:

- `state`: the new circuit breaker state (`OPEN`, `CLOSED`, `HALF_OPEN`)

- `name`: the name of the circuit breaker

- `failures`: the number of failures

- `node`: the identifier of the node (`local` if Vert.x is not running in cluster mode)

- metrics

## The half-open state

When the circuit breaker is `open`, calls to the circuit breaker fail immediately, without any attempt to execute the real operation. After a suitable amount of time (configured by `setResetTimeout`), the circuit breaker decides that the operation has a chance of succeeding, so it goes into the `half-open` state. In this state, the next call to the circuit breaker is allowed to execute the guarded operation. Should the call succeed, the circuit breaker resets and returns to the `closed` state, ready for more routine operation. If this trial call fails, however, the circuit breaker returns to the `open` state until another timeout elapses.

## Using Resilience4j

Resilience4j is a popular library that implements common fault tolerance strategies:

- bulkhead (concurrency limiter)

- circuit breaker

- rate limiter

- retry

- time limiter (timeout)

A how-to has been published that demonstrates the usage of Resilience4j with Vert.x. The repository of that how-to contains Vert.x adapters for all the fault tolerance strategies listed above. These adapters glue together the Resilience4j API and Vert.x `Future`s.

| Resilience4j 2.0 requires Java 17.