Skip to content

Commit

Permalink
fix: Don't use Status for managing breaker state
Browse files Browse the repository at this point in the history
The `Status` object is really only for keeping track of various events
that occur within the breaker. The CircuitBreaker doesn't really need to
manage those counts. And it especially doesn't need to depend on them
for managing state.

Move `Status` to its own module, and have it use breaker events to
manage its own stats.
  • Loading branch information
lance committed Nov 1, 2016
1 parent 3f41de6 commit 8c4c659
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 32 deletions.
40 changes: 10 additions & 30 deletions lib/circuit.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
'use strict';

const EventEmitter = require('events');
const Status = require('./status');

const STATE = Symbol('state');
const OPEN = Symbol('open');
const CLOSED = Symbol('closed');
const HALF_OPEN = Symbol('half-open');
const PENDING_CLOSE = Symbol('pending-close');
const FALLBACK_FUNCTION = Symbol('fallback');
const NUM_FAILURES = Symbol('num-failures');

class CircuitBreaker extends EventEmitter {
constructor (action, options) {
super();
if (typeof action !== 'function') this.action = () => action;
else this.action = action;
this.options = options;
this.status = new Status();
this.status = new Status(this);
this.Promise = options.Promise;
this[STATE] = CLOSED;
this[FALLBACK_FUNCTION] = null;
this[PENDING_CLOSE] = false;
this[NUM_FAILURES] = 0;

function _startTimer (circuit) {
return () =>
Expand All @@ -29,35 +32,22 @@ class CircuitBreaker extends EventEmitter {
}, circuit.options.resetTimeout).unref();
}

function _succeed (circuit) {
return () => {
circuit.status.successes++;
circuit.close();
};
}

function _fail (circuit) {
return () => {
circuit.status.failures++;
circuit.open();
};
}

this.on('open', _startTimer(this));
this.on('success', _succeed(this));
this.on('failure', _fail(this));
this.on('success', () => this.close());
this.on('failure', () => this.open());
}

close () {
this.status.failures = 0;
this[NUM_FAILURES] = 0;
if (this[STATE] !== CLOSED) {
this[STATE] = CLOSED;
this.emit('close');
}
}

open () {
if (this.status.failures >= this.options.maxFailures &&
this[NUM_FAILURES] += 1;
if (this[NUM_FAILURES] >= this.options.maxFailures &&
this[STATE] !== OPEN) {
this[STATE] = OPEN;
this.emit('open');
Expand All @@ -81,7 +71,6 @@ class CircuitBreaker extends EventEmitter {
}

fire () {
this.status.fires++;
this.emit('fire');
const args = Array.prototype.slice.call(arguments);

Expand Down Expand Up @@ -116,20 +105,11 @@ function failFast (circuit, err, args) {
circuit.emit('failure', err);
if (circuit[FALLBACK_FUNCTION]) {
return new circuit.Promise((resolve, reject) => {
circuit.status.fallbacks++;
circuit.emit('fallback');
resolve(circuit[FALLBACK_FUNCTION].apply(circuit[FALLBACK_FUNCTION], args));
});
}
return circuit.Promise.reject.apply(null, [err]);
}

class Status {
constructor () {
this.failures = 0;
this.fallbacks = 0;
this.successes = 0;
this.fires = 0;
}
}

module.exports = exports = CircuitBreaker;
21 changes: 21 additions & 0 deletions lib/status.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

const CIRCUIT_BREAKER = Symbol('circuit-breaker');

class Status {
constructor (circuit) {
this.failures = 0;
this.fallbacks = 0;
this.successes = 0;
this.rejects = 0;
this.fires = 0;
this[CIRCUIT_BREAKER] = circuit;
circuit.on('success', () => this.successes++);
circuit.on('failure', () => this.failures++);
circuit.on('fallback', () => this.fallbacks++);
circuit.on('fire', () => this.fires++);
circuit.on('reject', () => this.rejects++);
}
}

module.exports = exports = Status;
6 changes: 4 additions & 2 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,11 @@ test('Breaker resets for circuits with a fallback function', (t) => {
setTimeout(() => {
breaker.fire(100)
.then((arg) => t.equals(arg, 100))
.then(t.end);
.then(t.end)
.catch(t.fail);
}, resetTimeout * 1.25);
});
})
.catch(t.fail);
});

test('Executes fallback action, if one exists, when breaker is open', (t) => {
Expand Down

0 comments on commit 8c4c659

Please sign in to comment.