Skip to content

Commit

Permalink
feat: prefer an error percentage threshold
Browse files Browse the repository at this point in the history
Until now, the circuit breaker could only be configured to trip after a
fixed number of failures. This is problematic, because those failures
may accumulate over a long period of time, causing the circuit to open
once the threshold has been hit - even if the failure percentage is
very, very low.

Now when using `options.maxFailures` a deprecation warning is printed to
the error console. The new option is `errorThresholdPercentage`.

Fixes: #37
  • Loading branch information
lance committed Apr 3, 2017
1 parent 3052f23 commit 245d47b
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 6 deletions.
16 changes: 10 additions & 6 deletions lib/circuit.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ 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');
const STATUS = Symbol('status');
const NAME = Symbol('name');
const GROUP = Symbol('group');
Expand Down Expand Up @@ -45,20 +44,22 @@ class CircuitBreaker extends EventEmitter {
this.options = options;
this.options.rollingCountTimeout = options.rollingCountTimeout || 10000;
this.options.rollingCountBuckets = options.rollingCountBuckets || 10;
this.options.errorThresholdPercentage = 50;
this.Promise = options.Promise;

this[STATUS] = new Status(this.options);
this[STATE] = CLOSED;
this[FALLBACK_FUNCTION] = null;
this[PENDING_CLOSE] = false;
this[NUM_FAILURES] = 0;
this[NAME] = options.name || action.name || nextName();
this[GROUP] = options.group || this[NAME];

if (typeof action !== 'function') {
this.action = _ => this.Promise.resolve(action);
} else this.action = action;

if (options.maxFailures) console.error('options.maxFailures is deprecated. Please use options.errorThresholdPercentage');

const increment = property => _ => this[STATUS].increment(property);

this.on('success', increment('successes'));
Expand Down Expand Up @@ -102,7 +103,6 @@ class CircuitBreaker extends EventEmitter {
* @fires CircuitBreaker#close
*/
close () {
this[NUM_FAILURES] = 0;
this[PENDING_CLOSE] = false;
if (this[STATE] !== CLOSED) {
this[STATE] = CLOSED;
Expand Down Expand Up @@ -329,16 +329,20 @@ function fallback (circuit, err, args) {

function fail (circuit, err, args) {
/**
* Emitted when the circuit breaker action fails,
* Emitted when the circuit breaker action fails
* or when the circuit is fired while open.
* @event CircuitBreaker#failure
*/
circuit.emit('failure', err);
circuit[NUM_FAILURES] += 1;

if (circuit[NUM_FAILURES] >= circuit.options.maxFailures) {
// check stats to see if the circuit should be opened
const stats = circuit.stats;
const errorRate = stats.failures / stats.fires * 100;
if (errorRate > circuit.options.errorThresholdPercentage ||
circuit.options.maxFailures >= stats.failures) {
circuit.open();
}

return circuit.Promise.reject.apply(null, [err]);
}

Expand Down
12 changes: 12 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,18 @@ test('CircuitBreaker fallback as a CircuitBreaker', (t) => {
.then(t.end);
});

test('options.maxFailures should be deprecated', (t) => {
const options = { maxFailures: 1 };
const originalLog = console.error;
console.error = (msg) => {
t.equals(msg, 'options.maxFailures is deprecated. Please use options.errorThresholdPercentage');
// restore console.error
console.error = originalLog;
t.end();
};
cb(passFail, options);
});

/**
* Returns a promise that resolves if the parameter
* 'x' evaluates to >= 0. Otherwise the returned promise fails.
Expand Down

2 comments on commit 245d47b

@john681611
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So no backward compatibility for max failures. We still need to use it for testing purposes

@lance
Copy link
Member Author

@lance lance commented on 245d47b Aug 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@john681611 maxFailures is still supported, but currently deprecated. If you are experiencing a problem with it, please let us know with an issue.

Please sign in to comment.