Part of fp-ts ecosystem.
TypeScript implementation of Circuit Breaker pattern. Adaptation of Glue.CircuitBreaker module from Haskell.
You can read a bit more abou the pattern and this implementation in my article.
First of all, you need to install the package:
npm install circuit-breaker-monad
Then import the main circuitBreaker
function:
import { circuitBreaker } from 'circuit-breaker-monad/lib';
This function returns a Reader
, which, given the corresponding BreakerOptions
, creates an enhanced fetcher – a function which takes any Lazy<Promise<T>>
instance and returns a tuple of IORef<BreakerStatus>
and TaskEither<Error, T>
. The first element of this tuple is current circuit breaker status, implemented via IORef
(mutable reference in the IO monad), and the second element of the tuple is ready-to-be-called TaskEither
.
Let's look at the usage example:
import { fold } from 'fp-ts/lib/Either';
import { IORef } from 'fp-ts/lib/IORef';
import { circuitBreaker, defaultBreakerOptions } from 'circuit-breaker-monad/lib';
import { BreakerClosed } from 'circuit-breaker-monad/lib/types';
const fetcher = circuitBreaker<Response>()(defaultBreakerOptions);
const main = async () => {
const request = () => fetch('http://my-domain.com/my-data.json').then((res) => res.json());
const breakerState = new IORef(new BreakerClosed(0)); // initial breaker state
const [result, ref] = fetcher({ request, breakerState });
const response = await result();
fold(
(e: Error) => { },
(myData) => { },
)(response);
// ref :: BreakerClosed { errorCount: 0 }
// result :: TaskEither<Error, Response>
// response :: Either<Error, Response>
// myData :: TMyJsonData
};
The ref
variable is resulting circuit breaker status, which can be passed to the next call, so you take the full control over what's going on inside the circuit breaker:
const [ref, result] = fetcher(promise);
const myData1 = await result();
const [, result2] = fetcher(promise, ref);
const myData2 = await result2();
// here `ref` may be 'Open' if the second call to the service has failed
Circuit breaker may be configured by passing these parameters to the Reader:
maxBreakerFailures
- how many times the underlying service must fail in the given window before the circuit opens;resetTimeoutSecs
- the window of time in which the underlying service must fail for the circuit to open, seconds;breakerDescription
- description that is attached to the failure so as to identify the particular circuit.
Default options are:
export const defaultBreakerOptions: BreakerOptions = {
maxBreakerFailures: 3,
resetTimeoutSecs: 60,
breakerDescription: 'Circuit breaker is closed',
};