CircuitBreaker.prototype._startTicker = function() {
var self = this;
var bucketIndex = 0;
var bucketDuration = this.windowDuration / this.numBuckets;
var tick = function() {
if (self._buckets.length > self.numBuckets) {
self._buckets.shift();
}
bucketIndex++;
if (bucketIndex > self.numBuckets) {
bucketIndex = 0;
if (self.isOpen()) {
self._state = CircuitBreaker.HALF_OPEN;
}
}
self._buckets.push(self._createBucket());
};
setInterval(tick, bucketDuration);
};
CircuitBreaker.prototype._createBucket = function() {
return { failures: 0, successes: 0, timeouts: 0, shortCircuits: 0 };
};
CircuitBreaker.prototype._lastBucket = function() {
return this._buckets[this._buckets.length - 1];
};
CircuitBreaker.prototype._executeCommand = function(command) {
var self = this;
var timeout;
var increment = function(prop) {
return function() {
if (!timeout) { return; }
var bucket = self._lastBucket();
bucket[prop]++;
if (self._forced == null) {
self._updateState();
}
clearTimeout(timeout);
timeout = null;
};
};
timeout = setTimeout(increment('timeouts'), this.timeoutDuration);
command(increment('successes'), increment('failures'));
};
CircuitBreaker.prototype._executeFallback = function(fallback) {
fallback();
var bucket = this._lastBucket();
bucket.shortCircuits++;
};
CircuitBreaker.prototype._calculateMetrics = function() {
var totalCount = 0, errorCount = 0, errorPercentage = 0;
for (var i = 0, l = this._buckets.length; i < l; i++) {
var bucket = this._buckets[i];
var errors = (bucket.failures + bucket.timeouts);
errorCount += errors;
totalCount += (errors + bucket.successes);
}
errorPercentage = (errorCount / (totalCount > 0 ? totalCount : 1)) * 100;
return { totalCount: totalCount, errorCount: errorCount, errorPercentage: errorPercentage };
};
CircuitBreaker.prototype._updateState = function() {
var metrics = this._calculateMetrics();
if (this._state == CircuitBreaker.HALF_OPEN) {
var lastCommandFailed = !this._lastBucket().successes && metrics.errorCount > 0;
if (lastCommandFailed) {
this._state = CircuitBreaker.OPEN;
}
else {
this._state = CircuitBreaker.CLOSED;
this.onCircuitClose(metrics);
}
}
else {
var overErrorThreshold = metrics.errorPercentage > this.errorThreshold;
var overVolumeThreshold = metrics.totalCount > this.volumeThreshold;
var overThreshold = overVolumeThreshold && overErrorThreshold;
if (overThreshold) {
this._state = CircuitBreaker.OPEN;
this.onCircuitOpen(metrics);
}
}
};
var assign = function(name, obj) {
var commonJS = typeof module != 'undefined' && module.exports;
if (commonJS) {
module.exports = obj;
}
else {
window[name] = obj;
}
};
assign('CircuitBreaker', CircuitBreaker);
})();