Simulator first commit
This commit is contained in:
65
node_modules/backoff/lib/backoff.js
generated
vendored
Normal file
65
node_modules/backoff/lib/backoff.js
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2012 Mathieu Turcotte
|
||||
// Licensed under the MIT license.
|
||||
|
||||
var events = require('events');
|
||||
var precond = require('precond');
|
||||
var util = require('util');
|
||||
|
||||
// A class to hold the state of a backoff operation. Accepts a backoff strategy
|
||||
// to generate the backoff delays.
|
||||
function Backoff(backoffStrategy) {
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
this.backoffStrategy_ = backoffStrategy;
|
||||
this.maxNumberOfRetry_ = -1;
|
||||
this.backoffNumber_ = 0;
|
||||
this.backoffDelay_ = 0;
|
||||
this.timeoutID_ = -1;
|
||||
|
||||
this.handlers = {
|
||||
backoff: this.onBackoff_.bind(this)
|
||||
};
|
||||
}
|
||||
util.inherits(Backoff, events.EventEmitter);
|
||||
|
||||
// Sets a limit, greater than 0, on the maximum number of backoffs. A 'fail'
|
||||
// event will be emitted when the limit is reached.
|
||||
Backoff.prototype.failAfter = function(maxNumberOfRetry) {
|
||||
precond.checkArgument(maxNumberOfRetry > 0,
|
||||
'Expected a maximum number of retry greater than 0 but got %s.',
|
||||
maxNumberOfRetry);
|
||||
|
||||
this.maxNumberOfRetry_ = maxNumberOfRetry;
|
||||
};
|
||||
|
||||
// Starts a backoff operation. Accepts an optional parameter to let the
|
||||
// listeners know why the backoff operation was started.
|
||||
Backoff.prototype.backoff = function(err) {
|
||||
precond.checkState(this.timeoutID_ === -1, 'Backoff in progress.');
|
||||
|
||||
if (this.backoffNumber_ === this.maxNumberOfRetry_) {
|
||||
this.emit('fail', err);
|
||||
this.reset();
|
||||
} else {
|
||||
this.backoffDelay_ = this.backoffStrategy_.next();
|
||||
this.timeoutID_ = setTimeout(this.handlers.backoff, this.backoffDelay_);
|
||||
this.emit('backoff', this.backoffNumber_, this.backoffDelay_, err);
|
||||
}
|
||||
};
|
||||
|
||||
// Handles the backoff timeout completion.
|
||||
Backoff.prototype.onBackoff_ = function() {
|
||||
this.timeoutID_ = -1;
|
||||
this.emit('ready', this.backoffNumber_, this.backoffDelay_);
|
||||
this.backoffNumber_++;
|
||||
};
|
||||
|
||||
// Stops any backoff operation and resets the backoff delay to its inital value.
|
||||
Backoff.prototype.reset = function() {
|
||||
this.backoffNumber_ = 0;
|
||||
this.backoffStrategy_.reset();
|
||||
clearTimeout(this.timeoutID_);
|
||||
this.timeoutID_ = -1;
|
||||
};
|
||||
|
||||
module.exports = Backoff;
|
190
node_modules/backoff/lib/function_call.js
generated
vendored
Normal file
190
node_modules/backoff/lib/function_call.js
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
// Copyright (c) 2012 Mathieu Turcotte
|
||||
// Licensed under the MIT license.
|
||||
|
||||
var events = require('events');
|
||||
var precond = require('precond');
|
||||
var util = require('util');
|
||||
|
||||
var Backoff = require('./backoff');
|
||||
var FibonacciBackoffStrategy = require('./strategy/fibonacci');
|
||||
|
||||
// Wraps a function to be called in a backoff loop.
|
||||
function FunctionCall(fn, args, callback) {
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
precond.checkIsFunction(fn, 'Expected fn to be a function.');
|
||||
precond.checkIsArray(args, 'Expected args to be an array.');
|
||||
precond.checkIsFunction(callback, 'Expected callback to be a function.');
|
||||
|
||||
this.function_ = fn;
|
||||
this.arguments_ = args;
|
||||
this.callback_ = callback;
|
||||
this.lastResult_ = [];
|
||||
this.numRetries_ = 0;
|
||||
|
||||
this.backoff_ = null;
|
||||
this.strategy_ = null;
|
||||
this.failAfter_ = -1;
|
||||
this.retryPredicate_ = FunctionCall.DEFAULT_RETRY_PREDICATE_;
|
||||
|
||||
this.state_ = FunctionCall.State_.PENDING;
|
||||
}
|
||||
util.inherits(FunctionCall, events.EventEmitter);
|
||||
|
||||
// States in which the call can be.
|
||||
FunctionCall.State_ = {
|
||||
// Call isn't started yet.
|
||||
PENDING: 0,
|
||||
// Call is in progress.
|
||||
RUNNING: 1,
|
||||
// Call completed successfully which means that either the wrapped function
|
||||
// returned successfully or the maximal number of backoffs was reached.
|
||||
COMPLETED: 2,
|
||||
// The call was aborted.
|
||||
ABORTED: 3
|
||||
};
|
||||
|
||||
// The default retry predicate which considers any error as retriable.
|
||||
FunctionCall.DEFAULT_RETRY_PREDICATE_ = function(err) {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Checks whether the call is pending.
|
||||
FunctionCall.prototype.isPending = function() {
|
||||
return this.state_ == FunctionCall.State_.PENDING;
|
||||
};
|
||||
|
||||
// Checks whether the call is in progress.
|
||||
FunctionCall.prototype.isRunning = function() {
|
||||
return this.state_ == FunctionCall.State_.RUNNING;
|
||||
};
|
||||
|
||||
// Checks whether the call is completed.
|
||||
FunctionCall.prototype.isCompleted = function() {
|
||||
return this.state_ == FunctionCall.State_.COMPLETED;
|
||||
};
|
||||
|
||||
// Checks whether the call is aborted.
|
||||
FunctionCall.prototype.isAborted = function() {
|
||||
return this.state_ == FunctionCall.State_.ABORTED;
|
||||
};
|
||||
|
||||
// Sets the backoff strategy to use. Can only be called before the call is
|
||||
// started otherwise an exception will be thrown.
|
||||
FunctionCall.prototype.setStrategy = function(strategy) {
|
||||
precond.checkState(this.isPending(), 'FunctionCall in progress.');
|
||||
this.strategy_ = strategy;
|
||||
return this; // Return this for chaining.
|
||||
};
|
||||
|
||||
// Sets the predicate which will be used to determine whether the errors
|
||||
// returned from the wrapped function should be retried or not, e.g. a
|
||||
// network error would be retriable while a type error would stop the
|
||||
// function call.
|
||||
FunctionCall.prototype.retryIf = function(retryPredicate) {
|
||||
precond.checkState(this.isPending(), 'FunctionCall in progress.');
|
||||
this.retryPredicate_ = retryPredicate;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Returns all intermediary results returned by the wrapped function since
|
||||
// the initial call.
|
||||
FunctionCall.prototype.getLastResult = function() {
|
||||
return this.lastResult_.concat();
|
||||
};
|
||||
|
||||
// Returns the number of times the wrapped function call was retried.
|
||||
FunctionCall.prototype.getNumRetries = function() {
|
||||
return this.numRetries_;
|
||||
};
|
||||
|
||||
// Sets the backoff limit.
|
||||
FunctionCall.prototype.failAfter = function(maxNumberOfRetry) {
|
||||
precond.checkState(this.isPending(), 'FunctionCall in progress.');
|
||||
this.failAfter_ = maxNumberOfRetry;
|
||||
return this; // Return this for chaining.
|
||||
};
|
||||
|
||||
// Aborts the call.
|
||||
FunctionCall.prototype.abort = function() {
|
||||
if (this.isCompleted() || this.isAborted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isRunning()) {
|
||||
this.backoff_.reset();
|
||||
}
|
||||
|
||||
this.state_ = FunctionCall.State_.ABORTED;
|
||||
this.lastResult_ = [new Error('Backoff aborted.')];
|
||||
this.emit('abort');
|
||||
this.doCallback_();
|
||||
};
|
||||
|
||||
// Initiates the call to the wrapped function. Accepts an optional factory
|
||||
// function used to create the backoff instance; used when testing.
|
||||
FunctionCall.prototype.start = function(backoffFactory) {
|
||||
precond.checkState(!this.isAborted(), 'FunctionCall is aborted.');
|
||||
precond.checkState(this.isPending(), 'FunctionCall already started.');
|
||||
|
||||
var strategy = this.strategy_ || new FibonacciBackoffStrategy();
|
||||
|
||||
this.backoff_ = backoffFactory ?
|
||||
backoffFactory(strategy) :
|
||||
new Backoff(strategy);
|
||||
|
||||
this.backoff_.on('ready', this.doCall_.bind(this, true /* isRetry */));
|
||||
this.backoff_.on('fail', this.doCallback_.bind(this));
|
||||
this.backoff_.on('backoff', this.handleBackoff_.bind(this));
|
||||
|
||||
if (this.failAfter_ > 0) {
|
||||
this.backoff_.failAfter(this.failAfter_);
|
||||
}
|
||||
|
||||
this.state_ = FunctionCall.State_.RUNNING;
|
||||
this.doCall_(false /* isRetry */);
|
||||
};
|
||||
|
||||
// Calls the wrapped function.
|
||||
FunctionCall.prototype.doCall_ = function(isRetry) {
|
||||
if (isRetry) {
|
||||
this.numRetries_++;
|
||||
}
|
||||
var eventArgs = ['call'].concat(this.arguments_);
|
||||
events.EventEmitter.prototype.emit.apply(this, eventArgs);
|
||||
var callback = this.handleFunctionCallback_.bind(this);
|
||||
this.function_.apply(null, this.arguments_.concat(callback));
|
||||
};
|
||||
|
||||
// Calls the wrapped function's callback with the last result returned by the
|
||||
// wrapped function.
|
||||
FunctionCall.prototype.doCallback_ = function() {
|
||||
this.callback_.apply(null, this.lastResult_);
|
||||
};
|
||||
|
||||
// Handles wrapped function's completion. This method acts as a replacement
|
||||
// for the original callback function.
|
||||
FunctionCall.prototype.handleFunctionCallback_ = function() {
|
||||
if (this.isAborted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
this.lastResult_ = args; // Save last callback arguments.
|
||||
events.EventEmitter.prototype.emit.apply(this, ['callback'].concat(args));
|
||||
|
||||
var err = args[0];
|
||||
if (err && this.retryPredicate_(err)) {
|
||||
this.backoff_.backoff(err);
|
||||
} else {
|
||||
this.state_ = FunctionCall.State_.COMPLETED;
|
||||
this.doCallback_();
|
||||
}
|
||||
};
|
||||
|
||||
// Handles the backoff event by reemitting it.
|
||||
FunctionCall.prototype.handleBackoff_ = function(number, delay, err) {
|
||||
this.emit('backoff', number, delay, err);
|
||||
};
|
||||
|
||||
module.exports = FunctionCall;
|
41
node_modules/backoff/lib/strategy/exponential.js
generated
vendored
Normal file
41
node_modules/backoff/lib/strategy/exponential.js
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2012 Mathieu Turcotte
|
||||
// Licensed under the MIT license.
|
||||
|
||||
var util = require('util');
|
||||
var precond = require('precond');
|
||||
|
||||
var BackoffStrategy = require('./strategy');
|
||||
|
||||
// Exponential backoff strategy.
|
||||
function ExponentialBackoffStrategy(options) {
|
||||
BackoffStrategy.call(this, options);
|
||||
this.backoffDelay_ = 0;
|
||||
this.nextBackoffDelay_ = this.getInitialDelay();
|
||||
this.factor_ = ExponentialBackoffStrategy.DEFAULT_FACTOR;
|
||||
|
||||
if (options && options.factor !== undefined) {
|
||||
precond.checkArgument(options.factor > 1,
|
||||
'Exponential factor should be greater than 1 but got %s.',
|
||||
options.factor);
|
||||
this.factor_ = options.factor;
|
||||
}
|
||||
}
|
||||
util.inherits(ExponentialBackoffStrategy, BackoffStrategy);
|
||||
|
||||
// Default multiplication factor used to compute the next backoff delay from
|
||||
// the current one. The value can be overridden by passing a custom factor as
|
||||
// part of the options.
|
||||
ExponentialBackoffStrategy.DEFAULT_FACTOR = 2;
|
||||
|
||||
ExponentialBackoffStrategy.prototype.next_ = function() {
|
||||
this.backoffDelay_ = Math.min(this.nextBackoffDelay_, this.getMaxDelay());
|
||||
this.nextBackoffDelay_ = this.backoffDelay_ * this.factor_;
|
||||
return this.backoffDelay_;
|
||||
};
|
||||
|
||||
ExponentialBackoffStrategy.prototype.reset_ = function() {
|
||||
this.backoffDelay_ = 0;
|
||||
this.nextBackoffDelay_ = this.getInitialDelay();
|
||||
};
|
||||
|
||||
module.exports = ExponentialBackoffStrategy;
|
28
node_modules/backoff/lib/strategy/fibonacci.js
generated
vendored
Normal file
28
node_modules/backoff/lib/strategy/fibonacci.js
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2012 Mathieu Turcotte
|
||||
// Licensed under the MIT license.
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var BackoffStrategy = require('./strategy');
|
||||
|
||||
// Fibonacci backoff strategy.
|
||||
function FibonacciBackoffStrategy(options) {
|
||||
BackoffStrategy.call(this, options);
|
||||
this.backoffDelay_ = 0;
|
||||
this.nextBackoffDelay_ = this.getInitialDelay();
|
||||
}
|
||||
util.inherits(FibonacciBackoffStrategy, BackoffStrategy);
|
||||
|
||||
FibonacciBackoffStrategy.prototype.next_ = function() {
|
||||
var backoffDelay = Math.min(this.nextBackoffDelay_, this.getMaxDelay());
|
||||
this.nextBackoffDelay_ += this.backoffDelay_;
|
||||
this.backoffDelay_ = backoffDelay;
|
||||
return backoffDelay;
|
||||
};
|
||||
|
||||
FibonacciBackoffStrategy.prototype.reset_ = function() {
|
||||
this.nextBackoffDelay_ = this.getInitialDelay();
|
||||
this.backoffDelay_ = 0;
|
||||
};
|
||||
|
||||
module.exports = FibonacciBackoffStrategy;
|
80
node_modules/backoff/lib/strategy/strategy.js
generated
vendored
Normal file
80
node_modules/backoff/lib/strategy/strategy.js
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2012 Mathieu Turcotte
|
||||
// Licensed under the MIT license.
|
||||
|
||||
var events = require('events');
|
||||
var util = require('util');
|
||||
|
||||
function isDef(value) {
|
||||
return value !== undefined && value !== null;
|
||||
}
|
||||
|
||||
// Abstract class defining the skeleton for the backoff strategies. Accepts an
|
||||
// object holding the options for the backoff strategy:
|
||||
//
|
||||
// * `randomisationFactor`: The randomisation factor which must be between 0
|
||||
// and 1 where 1 equates to a randomization factor of 100% and 0 to no
|
||||
// randomization.
|
||||
// * `initialDelay`: The backoff initial delay in milliseconds.
|
||||
// * `maxDelay`: The backoff maximal delay in milliseconds.
|
||||
function BackoffStrategy(options) {
|
||||
options = options || {};
|
||||
|
||||
if (isDef(options.initialDelay) && options.initialDelay < 1) {
|
||||
throw new Error('The initial timeout must be greater than 0.');
|
||||
} else if (isDef(options.maxDelay) && options.maxDelay < 1) {
|
||||
throw new Error('The maximal timeout must be greater than 0.');
|
||||
}
|
||||
|
||||
this.initialDelay_ = options.initialDelay || 100;
|
||||
this.maxDelay_ = options.maxDelay || 10000;
|
||||
|
||||
if (this.maxDelay_ <= this.initialDelay_) {
|
||||
throw new Error('The maximal backoff delay must be ' +
|
||||
'greater than the initial backoff delay.');
|
||||
}
|
||||
|
||||
if (isDef(options.randomisationFactor) &&
|
||||
(options.randomisationFactor < 0 || options.randomisationFactor > 1)) {
|
||||
throw new Error('The randomisation factor must be between 0 and 1.');
|
||||
}
|
||||
|
||||
this.randomisationFactor_ = options.randomisationFactor || 0;
|
||||
}
|
||||
|
||||
// Gets the maximal backoff delay.
|
||||
BackoffStrategy.prototype.getMaxDelay = function() {
|
||||
return this.maxDelay_;
|
||||
};
|
||||
|
||||
// Gets the initial backoff delay.
|
||||
BackoffStrategy.prototype.getInitialDelay = function() {
|
||||
return this.initialDelay_;
|
||||
};
|
||||
|
||||
// Template method that computes and returns the next backoff delay in
|
||||
// milliseconds.
|
||||
BackoffStrategy.prototype.next = function() {
|
||||
var backoffDelay = this.next_();
|
||||
var randomisationMultiple = 1 + Math.random() * this.randomisationFactor_;
|
||||
var randomizedDelay = Math.round(backoffDelay * randomisationMultiple);
|
||||
return randomizedDelay;
|
||||
};
|
||||
|
||||
// Computes and returns the next backoff delay. Intended to be overridden by
|
||||
// subclasses.
|
||||
BackoffStrategy.prototype.next_ = function() {
|
||||
throw new Error('BackoffStrategy.next_() unimplemented.');
|
||||
};
|
||||
|
||||
// Template method that resets the backoff delay to its initial value.
|
||||
BackoffStrategy.prototype.reset = function() {
|
||||
this.reset_();
|
||||
};
|
||||
|
||||
// Resets the backoff delay to its initial value. Intended to be overridden by
|
||||
// subclasses.
|
||||
BackoffStrategy.prototype.reset_ = function() {
|
||||
throw new Error('BackoffStrategy.reset_() unimplemented.');
|
||||
};
|
||||
|
||||
module.exports = BackoffStrategy;
|
Reference in New Issue
Block a user