@syrf/circuit-breaker
v1.0.2
Published
Circuit breaker for promised based service
Downloads
7
Readme
circuit-breaker
Circuit breaker library for NodeJs to handle and monitor unstable or slow operation like http request to external service. circuit-breaker breaks internal circuit when average failure has reached a threshold value and provide fallback service while backing off the service so it can recover
States
There are three states of circuit breakers
- closed : Normal state of the circuit
- open : State when failure threshold reached, call to the handled service will immediately return CircuitBroken error or execute fallback service
- half open : State after interval passed and circuit will try to execute service again. if success threshold reached circuit will close. if failure threshold reached again, circuit will open again
Examples
Basic Usage
const Breaker = require('circuit-breaker');
const unstableService = (parameter1, parameter2) => {
return new Promise((resolve, reject) => {
//call to an unstable service
console.log(parameter1, parameter2);
//will print hello world
});
};
const breaker = Breaker.create(unstableService)
.withFailureThreshold(0.3)
.withSuccessThreshold(0.5);
const parameter1 = 'Hello';
const parameter2 = 'World';
breaker.exec(parameter1, parameter2);
Fallback
const unstableService = (parameter1, parameter2) => {
return new Promise((resolve, reject) => {
//call to an unstable service
reject(new Error('Some Error'));
});
};
const fallbackService = (parameter1, parameter2) => {
return new Promise((resolve, reject) => {
console.log('Fallback ', parameter1, parameter2);
//will print Fallback hello world
});
};
const breaker =
Breaker.create(unstableService).withFallbackService(fallbackService);
const parameter1 = 'Hello';
const parameter2 = 'World';
breaker.exec(parameter1, parameter2);
Intervals
Interval define time span after circuit broke to switch to half open for retries
Fixed Interval
const breaker = Breaker.create(unstableService).withBackoffInterval({
interval: 10 * 1000,
});
Incremental Interval
const breaker = Breaker.create(unstableService).withBackoffInterval({
increment: 5 * 1000, //will increase interval by 5s every retry
});
Exponential Interval
Exponential interval will power the interval by the number of retry
const breaker = Breaker.create(unstableService).withBackoffInterval({
exponentInterval: true,
});
For more real case examples you can check the examples
directory
Constructor
circuit breaker accepts two arguments
- service :
function
which returnsPromise
- config : configuration object
Configurations
Available configuration options :
Config | Type | Default | Description
---|---|---|---
name|string
|defaultBrake|name of service. mostly used to identify service on global monitor
group |string
|defaultGroup|name of service group. mostly used to identify service on global monitor
failureThreshold |number
|0.5|average failure threshold to trigger circuit to break
successThreshold |number
|0.5|average success threshold to trigger circuit to recover
interval|number
|10000|Time interval in milliseconds to back off the service
increment|number
|0|Time interval increment in milliseconds. incremented when circuit is closed again after half open state
exponentInterval|boolean
|false|Flag to set interval to exponential if the service keeps on failing. the interval value in second will be powered to number of attempt to connect
timeout|number
|3000|Max execution time in milliseconds for handled service
fallback|method
|null|Fallback method expected to return promise
bucketSpan|number
|1000|time span of a bucket to should remain active
bucketCount|number
|60|number of buckets to retain in a rolling window
statsInterval|number
|5000|time interval between stats reporting
registerGlobal|boolean
|true|specify if circuit breaker should be regitered in global stats
Builder Methods Samples
List of builder methods to ease up configuring circuit breaker
- static
Breaker.create(service)
.withFallbackService(serviceFallback)
.withFailureThreshold(0.35)
.withSuccessThreshold(0.65)
.withBackoffInterval({ interval: 1000, increment : 2000, exponential : false })
.withBucket({ bucketSpan: 5000, bucketCount : 10 })
.withExecutionTimeout(3000)
.withStatsInterval(1000)
withBackoffInterval
and withBucket
accept object as parameter. every field of the object is optional, if value not provided, default value will be used
Methods
| Method | Argument(s) | Returns | Description |
| ----------------------- | ------------------------------------------ | ----------- | ----------------------------------------------------------- |
| static getGlobalStats | N/A | globalStats | Returns global status where all circuit breakers registered |
| exec | Same arguments with the registered service | Promise | Returns the same promise as the registered service |
| on | string
,function
| N/A | Standard EventEmitter listening |
Non Breaking Error
When handling service using library like axios, we often get expected error like 400 errors etc. but those kind of error is not considered as breaking errors because the error thrown does not indicate that the service is not working properly. to be able to handle the error just like normal error without opening the circuit NonBreakingError
can be thrown.
const restService = () => {
return new Promise((resolve, reject) => {
axios
.get('http://example.com/api')
.then(resolve)
.catch((err) => {
// check if the response is started with 400 (400,401,404 etc)
if (err?.response?.status?.toString()?.[0] === '4') {
// throw non breaking error
reject(new Breaker.NonBreakingError(err?.response));
} else {
// this will add failure count and opens circuit eventually
reject(err);
}
});
});
};
Events
List of events emitted by circuit breaker :
- exec : Event on service execution start
- failure : Event on service execution failed
- success : Event on service execution success
- timeout : Event on service execution timeout
- circuitClosed : Event when circuit closed
- circuitOpen : Event when circuit open
- circuitHalfOpen : Event when circuit half open
- stats : Event when circuit sends stats
Stats
Circuit breaker will emit stats event with latest stats data based on statsInterval
interval
Stats object sample :
{
name: 'defaultBrake',
group: 'defaultGroup',
state: 'CLOSED',
failureThreshold: 0.5,
successThreshold: 0.5,
nextAttempt: 1619424986009,
retryCount: 0,
bucketSpan: 1000,
bucketCount: 60,
increment: 0,
exponentInterval: false,
interval: 10000,
timeout: 3000,
stats: {
avgSuccess: 0.9,
avgFailure: 0.5,
failed : 10,
successful : 90,
total : 100,
shortCircuited : 0,
timedOut : 5,
avgLatency : 1023
},
}
Global Stats
Global stats will listen to the stats Event emitted by all registered circuit breaker and push the stats to a readable stream
const globalStats = Breaker.getGlobalStats();
globalStats.stream.on('data', (data) => {
//handle data
console.log(data);
});
Development
To develop locally clone the repo and use the following commands :
npm install
npm run test