diff --git a/.gitignore b/.gitignore index c2658d7..20de930 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules/ +.vscode/ diff --git a/index.js b/index.js index 6281fa6..ba267e2 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ var Backoff = require('./lib/backoff'); var ExponentialBackoffStrategy = require('./lib/strategy/exponential'); var FibonacciBackoffStrategy = require('./lib/strategy/fibonacci'); +var InverseExponentialBackoffStrategy = require('./lib/strategy/inverse_exponential'); var FunctionCall = require('./lib/function_call.js'); module.exports.Backoff = Backoff; @@ -21,6 +22,11 @@ module.exports.exponential = function(options) { return new Backoff(new ExponentialBackoffStrategy(options)); }; +// Constructs an inverse exponential backoff. +module.exports.inverseExponential = function(options) { + return new Backoff(new InverseExponentialBackoffStrategy(options)); +}; + // Constructs a FunctionCall for the given function and arguments. module.exports.call = function(fn, vargs, callback) { var args = Array.prototype.slice.call(arguments); diff --git a/lib/strategy/inverse_exponential.js b/lib/strategy/inverse_exponential.js new file mode 100644 index 0000000..5b8e912 --- /dev/null +++ b/lib/strategy/inverse_exponential.js @@ -0,0 +1,38 @@ +var util = require('util'); +var precond = require('precond'); + +var BackoffStrategy = require('./strategy'); + +// Inverse Exponential backoff strategy. +function InverseExponentialBackoffStrategy (options) { + BackoffStrategy.call(this, options); + this.backoffDelay_ = 512; + this.nextBackoffDelay_ = this.getInitialDelay(); + this.factor_ = InverseExponentialBackoffStrategy.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(InverseExponentialBackoffStrategy, BackoffStrategy); + +// Default division 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. +InverseExponentialBackoffStrategy.DEFAULT_FACTOR = 2; + +InverseExponentialBackoffStrategy.prototype.next_ = function () { + this.backoffDelay_ = Math.max(this.nextBackoffDelay_, 0); + this.nextBackoffDelay_ = parseInt(this.backoffDelay_ / this.factor_); + return this.backoffDelay_; +}; + +InverseExponentialBackoffStrategy.prototype.reset_ = function () { + this.backoffDelay_ = 512; + this.nextBackoffDelay_ = this.getInitialDelay(); +}; + +module.exports = InverseExponentialBackoffStrategy; diff --git a/package.json b/package.json index b6d6cb6..3727adf 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,38 @@ { - "name": "backoff", - "description": "Fibonacci and exponential backoffs.", - "version": "2.5.0", - "license": "MIT", - "author": "Mathieu Turcotte ", - "keywords": ["backoff", "retry", "fibonacci", "exponential"], - "repository": { - "type": "git", - "url": "https://github.com/MathieuTurcotte/node-backoff.git" - }, - "dependencies": { - "precond": "0.2" - }, - "devDependencies": { - "sinon": "1.10", - "nodeunit": "0.9" - }, - "scripts": { - "docco" : "docco lib/*.js lib/strategy/* index.js", - "pretest": "jshint lib/ tests/ examples/ index.js", - "test": "node_modules/nodeunit/bin/nodeunit tests/" - }, - "engines": { - "node": ">= 0.6" - }, - "files": [ - "index.js", - "lib", - "tests" - ] + "name": "backoff", + "description": "Fibonacci and exponential backoffs.", + "version": "2.5.0", + "license": "MIT", + "author": "Mathieu Turcotte ", + "keywords": [ + "backoff", + "retry", + "fibonacci", + "exponential" + ], + "repository": { + "type": "git", + "url": "https://github.com/MathieuTurcotte/node-backoff.git" + }, + "dependencies": { + "precond": "0.2" + }, + "devDependencies": { + "jshint": "2.9.5", + "nodeunit": "0.9", + "sinon": "1.10" + }, + "scripts": { + "docco": "docco lib/*.js lib/strategy/* index.js", + "pretest": "jshint lib/ tests/ examples/ index.js", + "test": "node_modules/nodeunit/bin/nodeunit tests/" + }, + "engines": { + "node": ">= 0.6" + }, + "files": [ + "index.js", + "lib", + "tests" + ] } diff --git a/tests/inverse_exponential_backoff_strategy.js b/tests/inverse_exponential_backoff_strategy.js new file mode 100644 index 0000000..e957edd --- /dev/null +++ b/tests/inverse_exponential_backoff_strategy.js @@ -0,0 +1,58 @@ +/* + * + * Licensed under the MIT license. + */ + +var sinon = require('sinon'); + +var InverseExponentialBackoffStrategy = require('../lib/strategy/inverse_exponential'); + +exports["InverseExponentialBackoffStrategy"] = { + + "backoff delays should follow an inverse exponential sequence": function(test) { + var strategy = new InverseExponentialBackoffStrategy({ + initialDelay: 512 + }); + + // sequence: x[i] = x[i-1] / 2. + var expectedDelays = [512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0]; + var actualDelays = expectedDelays.map(function () { + return strategy.next(); + }); + + test.deepEqual(expectedDelays, actualDelays, + 'Generated delays should follow an exponential sequence.'); + test.done(); + }, + + "backoff delay factor should be configurable": function (test) { + var strategy = new InverseExponentialBackoffStrategy({ + initialDelay: 1024, + factor: 4 + }); + + // Exponential sequence: x[i] = x[i-1] / 4. + var expectedDelays = [1024, 256, 64, 16, 4, 1, 0]; + var actualDelays = expectedDelays.map(function () { + return strategy.next(); + }); + + test.deepEqual(expectedDelays, actualDelays, + 'Generated delays should follow a configurable exponential sequence.'); + test.done(); + }, + + "backoff delays should restart from the initial delay after reset": function(test) { + var strategy = new InverseExponentialBackoffStrategy({ + initialDelay: 512 + }); + + strategy.next(); + strategy.reset(); + + var backoffDelay = strategy.next(); + test.equals(backoffDelay, 512, + 'Strategy should return the initial delay after reset.'); + test.done(); + } +};