Skip to content

Commit

Permalink
feat: add basic rolling stats to a circuit
Browse files Browse the repository at this point in the history
This replaces the existing cumulative behavior of a circuit's status, in
favor of snapshots every `X` milliseconds, where `X` defaults to 10000,
e.g. 10 seconds. This allows for rolling average calculations on
important statistics for Hystrix dashboard, and is in pursuit of
#32 (but does not
complete it).
  • Loading branch information
lance committed Mar 8, 2017
1 parent f08d46e commit 8fb9561
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 24 deletions.
4 changes: 4 additions & 0 deletions lib/circuit.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ const NAME = Symbol('name');
* opening.
* @param options.resetTimeout The time in milliseconds to wait before setting
* the breaker to `halfOpen` state, and trying the action again.
* @param options.rollingCountTimeout Sets the duration of the statistical
* rolling window, in milliseconds. This is how long Opossum keeps metrics for
* the circuit breaker to use and for publishing. Default: 10000
* @fires CircuitBreaker#halfOpen
*/
class CircuitBreaker extends EventEmitter {
constructor (action, options) {
super();
this.options = options;
this.options.rollingCountTimeout = options.rollingCountTimeout || 10000;
this.Promise = options.Promise;
this[STATUS] = new Status(this);
this[STATE] = CLOSED;
Expand Down
55 changes: 31 additions & 24 deletions lib/status.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,45 @@ const CIRCUIT_BREAKER = Symbol('circuit-breaker');
*/
class Status {
constructor (circuit) {
/**
* The number of times the breaker's action has failed
*/
this.failures = 0;
/**
* The number of times a fallback function has been executed
*/
this.fallbacks = 0;
/**
* The number of times the action for this breaker executed successfully
*/
this.successes = 0;
/**
* The number of times this breaker been rejected because it was fired, but in the open state.
*/
this.rejects = 0;
/**
* The number of times this circuit breaker has been fired
*/
this.fires = 0;
/**
* The number of times this circuit breaker has timed out
*/
this.timeouts = 0;
reset(this);
this[CIRCUIT_BREAKER] = circuit;
circuit.on('success', () => this.successes++);
circuit.on('failure', () => this.failures++);
circuit.on('fallback', () => this.fallbacks++);
circuit.on('timeout', () => this.timeouts++);
circuit.on('fire', () => this.fires++);
circuit.on('reject', () => this.rejects++);
const interval = setInterval(
() => reset(this), circuit.options.rollingCountTimeout);
if (typeof interval.unref === 'function') interval.unref();
}
}

function reset (status) {
/**
* The number of times the breaker's action has failed
*/
status.failures = 0;
/**
* The number of times a fallback function has been executed
*/
status.fallbacks = 0;
/**
* The number of times the action for this breaker executed successfully
*/
status.successes = 0;
/**
* The number of times this breaker been rejected because it was fired, but in the open state.
*/
status.rejects = 0;
/**
* The number of times this circuit breaker has been fired
*/
status.fires = 0;
/**
* The number of times this circuit breaker has timed out
*/
status.timeouts = 0;
}

module.exports = exports = Status;
18 changes: 18 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,24 @@ test('CircuitBreaker status', (t) => {
});
});

test('CircuitBreaker rolling counts', (t) => {
const breaker = cb(passFail, { rollingCountTimeout: 100 });
const deepEqual = (t, expected) => (actual) => t.deepEqual(actual, expected, 'expected status values');
Fidelity.all([
breaker.fire(10).then(deepEqual(t, 10)),
breaker.fire(20).then(deepEqual(t, 20)),
breaker.fire(30).then(deepEqual(t, 30))
])
.then(() =>
t.deepEqual(breaker.status.successes, 3, 'breaker succeeded 3 times'))
.then(() => {
setTimeout(() => {
t.deepEqual(breaker.status.successes, 0, 'breaker reset stats');
t.end();
}, 100);
});
});

test('CircuitBreaker fallback event', (t) => {
t.plan(1);
const breaker = cb(passFail, {maxFailures: 0});
Expand Down

0 comments on commit 8fb9561

Please sign in to comment.