This project is a lightweight and easy-to-use circuit breaker, designed for typescript and functional programming.
It has two different purposes:
- A production ready circuit breaker for typescript
- A tutorial to understand this useful resilience pattern through typescript
If this resilience pattern does not sounds familiar to you, take a look on these resources:
npm i circuit-breaker-typescript
Let's assume you have an http call and you want to fail-fast gracefully without waiting for TCP connection timeout in case of the service eventually is not available:
const unprotectedPromise = () => fetch(someUrl).then(response => response.json());
Protecting it is pretty straight forward:
const circuitBreaker = new CircuitBreaker();
const protectedPromise = circuitBreaker.protectPromise(unprotectedPromise);
//normal use
protectedPromise().then(...);
You have a function that you want to protect:
const unprotectedFunction = (params: Params) => {
// side effects that can eventually fail recurrently
};
Protecting it:
const circuitBreaker = new CircuitBreaker();
const protectedFunction = circuitBreaker.protectFunction(unprotectedFunction);
//normal use
protectedFunction(params)
Create a new instance of a circuit breaker. It accepts the following config options:
Number of errors after the circuit trips to open and starts short-circuiting requests and failing-fast.
Default Value: 5
Time in milliseconds in which after tripping to open the circuit will remain failing fast.
Default Value: 1000
const circuitBreaker = new CircuitBreaker({maxFailures: 10, resetTimeoutInMillis: 10000});
Circuit breaker will fail fast when any call enters to it during the open period, in these cases it will throw or return
an error Error('CircuitBreaker: fail-fast')
Please install npm
or yarn
.
yarn install
Tests are configured to fail if coverage go below 90%, you can see the report just running the tests:
yarn test
The motivation of this project was to understand this resilience pattern and provide a ready-for-production implementation in a some programming language.
These are some of the aspects that have been used or taken in account to design the solution:
- Language: typescript
- Functional programming in mind (Immutability, lambdas ...)
- Finite state machines
The main purpose of circuit breakers in software world is to provide the capability to detect failures in your system in order to handle the errors quickly and gracefully without waiting for problems. Another good aspect about them is that they prevent to cascade problems to another components/systems because they act as a walls when they are open.
-
Circuit Closed:
- Failures increase the failures counter
- Call Successes reset the failures to zero
- When the failures counter reaches the maxFailures threshold, the breaker trips to Open
-
Circuit Open:
- The circuit breaker fail fast always, calls are not made
- After the timeout expires the circuit enters to HalfOpen
-
Circuit HalfOpen:
- The first call that enters to the circuit in this state will be allowed:
- If the call succeeds, the breaker is reset back to Closed
- If the call fails, the breaker is trip back into Open
- The first call that enters to the circuit in this state will be allowed:
A finite state machine is a computational model that can be in exactly one of a finite number of states at any given time. The state machine can change from one state to another in response to some external inputs and the change from one state to another is called a transition.
Having in mind the definition, let's define the state machine:
- The state machine for circuit breaker will transition between the three different states, Closed, Open and HalfOpen.
- The state machine will receive the inputs of the app as events:
CallSucceed
: If the call succeed this event will be sent to the state machineCallFailed
: If the call failed this event will be sent to the state machineBeforeCall
: In order to detect if the reset timeout has expired when the circuit isOpen
we need either sent a signal or keep a timer, with the first option the system won't need to check periodically, it will react when a call is going to be made.
Current State | Event | Condition | Next State |
---|---|---|---|
Closed | CallSucceed |
- | Closed |
Closed | CallFailed |
under threshold | Closed |
Closed | CallFailed |
max failures reached | Open |
Closed | BeforeCall |
- | Closed |
Open | CallSucceed |
- | Open |
Open | CallFailed |
- | Open |
Open | BeforeCall |
timeout not exceeded | Open |
Open | BeforeCall |
timeout exceeded | HalfOpen |
HalfClosed | CallSucceed |
- | Closed |
HalfClosed | CallFailed |
- | Open |
HalfClosed | BeforeCall |
- | HalfOpen |
- States: The states in which the circuit can be
Closed
,Open
orHalfOpen
. - State machine: The immutable finite state machine that controls the transitions between the different states in response to some external inputs.
- Circuit breaker: Main class of the application, it is the public api, controls the general flow and keeps the state machine.
- Improve Circuit Breaker Config with a builder
- Create configuration to accept predicates to decide wheter a result of a function should be considered as a failure. Then the library could be used with monads like Either
- Add support for metrics/notify
- Add support for fallbacks