Am trying to implement a circuit breaker in node js as a singleton class using https://nodeshift.dev/opossum/
For some reason, breaker metrics always start from 0 and the circuit never opens after the threshold failure.
REF: https://github.com/nodeshift/opossum/issues/593 -> The author seems to suggest using closure to achieve singleton property but doesn't work for me. Any reason why or pointers I can check?
Quick test link: https://filebin.net/zbjhntvjnqwdobns (download and use the command (npm install && node src/singleton.js) to run it locally)
singleton.js
let Adapter = require('./adapter');
let routerHandlerWrapper = require('./handler-wrapper');
const start = async function () {
let i=0;
for (i = 0; i < 100; i++) {
const adapter = new Adapter(routerHandlerWrapper);
await adapter.callPartner(i);
console.log("attempt: " + i);
}
}
start();
handler-wrapper.js
const { breaker } = require('./circuit-breaker');
module.exports = function (routerController, next) {
console.log("Creating controller circuit");
const breakerController = breaker(routerController);
breakerController.on('open', () => {
console.log('The circuit is open ' + JSON.stringify(breakerController.stats));
});
return async function (req, res, i) {
try {
// console.log(breakerController.stats);
await breakerController.fire(req, res, i);
} catch (err) {
next(err);
}
};
};
circuit-breaker.js
const CircuitBreaker = require('opossum');
const circuitBreakerOptions = {
enabled: true,
timeout: 10000, // If our function takes longer than 10 seconds, trigger a failure
errorThresholdPercentage: 50, // When 50% of requests fail, trip the circuit
volumeThreshold: 5,
rollingCountTimeout: 300000,
resetTimeout: 20000 // After 60 seconds, try again.
}
const options = {
// errorFilter: err => ((!err || !err.code)
// ? false // open circuit breaker
// : (err.code >= 300 && err.code < 500))
...circuitBreakerOptions
};
module.exports = {
breaker: asyncFunc => new CircuitBreaker(asyncFunc, options)
};
adapter.js
class Adapter {
constructor(routerHandlerWrapper) {
this.routerHandlerWrapper = routerHandlerWrapper
}
async asyncFunctionThatCouldFail(x, y, i) {
if (i > 40) {
return await new Promise((resolve, reject) => setTimeout(() => resolve("positive"), 1000));
}
await new Promise((resolve, reject) => setTimeout(() => reject(new Error('timeout')), 1000));
console.log("the args: " + x + " " + y + " " + i);
throw "error";
}
async callPartner(i) {
const x = "value1", y = "value2";
let wrappedFunc = this.routerHandlerWrapper(this.asyncFunctionThatCouldFail, console.error);
await wrappedFunc(x, y, i);
}
}
module.exports = Adapter;