[][src]Crate recloser

recloser

A concurrent circuit breaker implemented with ring buffers.

The Recloser struct provides a call(...) method to wrap function calls that may fail, it will eagerly reject them when some failure_rate is reached, and it will allow them again after some time. A future aware version of call(...) is also available through an async::AsyncRecloser wrapper.

The API is largely based on failsafe and the ring buffer implementation on resilient4j.

Usage

The Recloser can be in three states:

  • Closed(RingBuffer(len)): The initial Recloser's state. At least len calls will be performed before calculating a failure_rate based on which transitions to Open(_) state may happen.
  • Open(duration): All calls will return Err(Error::Rejected) until duration has elapsed, then transition to HalfOpen(_) state will happen.
  • HalfOpen(RingBuffer(len)): At least len calls will be performed before calculating a failure_rate based on which transitions to either Closed(_) or Open(_) states will happen.

The state transition settings can be customized as follows:

use std::time::Duration;
use recloser::Recloser;

// Equivalent to Recloser::default()
let recloser = Recloser::custom()
   .error_rate(0.5)
   .closed_len(100)
   .half_open_len(10)
   .open_wait(Duration::from_secs(30))
   .build();

Wrapping dangerous function calls in order to control failure propagation:

use matches::assert_matches;
use recloser::{Recloser, Error};

// Performs 1 call before calculating failure_rate
let recloser = Recloser::custom().closed_len(1).build();

let f1 = || Err::<(), usize>(1);

// First call, just recorded as an error
let res = recloser.call(f1);
assert_matches!(res, Err(Error::Inner(1)));

// Now also computes failure_rate, that is 100% here
// Will transition to State::Open afterward
let res = recloser.call(f1);
assert_matches!(res, Err(Error::Inner(1)));

let f2 = || Err::<(), i64>(-1);

// All calls are rejected (while in State::Open)
let res = recloser.call(f2);
assert_matches!(res, Err(Error::Rejected));

It is also possible to discard some errors on a per call basis. This behavior is controlled by the ErrorPredicate<E>trait, which is already implemented for all Fn(&E) -> bool.

use matches::assert_matches;
use recloser::{Recloser, Error};

let recloser = Recloser::default();

let f = || Err::<(), usize>(1);

// Custom predicate that doesn't consider usize values as errors
let p = |_: &usize| false;

// Will not record resulting Err(1) as an error
let res = recloser.call_with(p, f);
assert_matches!(res, Err(Error::Inner(1)));

Wrapping functions that return Futures requires to use an AsyncRecloser that just wraps a regular Recloser.

use futures::future;
use recloser::{Recloser, Error};
use recloser::r#async::AsyncRecloser;

let recloser = AsyncRecloser::from(Recloser::default());

let future = future::lazy(|_| Err::<(), usize>(1));
let future = recloser.call(future);

Modules

async

Structs

AnyError

Considers any value as a failure.

Recloser

A concurrent cirbuit breaker based on RingBuffers that allows or rejects calls depending on the state it is in.

RecloserBuilder

A helper struct to build customized Recloser.

Enums

Error

Error returned by Recloser wrapped function calls.

Traits

ErrorPredicate

A trait used to determine whether an E should be considered as a failure.