diff --git a/lib/ExponentialBackoff.js b/lib/ExponentialBackoff.js new file mode 100644 index 0000000..f29325e --- /dev/null +++ b/lib/ExponentialBackoff.js @@ -0,0 +1,47 @@ +'use strict'; + +/** + * ExponentialBackoff class handles the logic for increasing or decreasing the delay between + * multiple requests. See https://en.wikipedia.org/wiki/Exponential_backoff + * + * @param {number} minimum_backoff the lowest permissible delay between requests. defaults to 50ms + * @param {number} exponent the factor by which to increse or decrease the backoff. defaults to 2 + * @param {number} starting_backoff the backoff between the first and second request. defaults to * 50ms + */ +var ExponentialBackoff = function(minimum_backoff, exponent, starting_backoff) { + this.minimum_backoff = minimum_backoff || 50; + this.exponent = exponent || 2.0; + this.starting_backoff = starting_backoff || this.minimum_backoff; + + this.backoff = this.starting_backoff; +}; + +/** + * Get the current backoff as a simple number + */ +ExponentialBackoff.prototype.getBackoff = function getBackoff() { + return this.backoff; +}; + + +/** + * Increase the backoff for the next request. This method should be called after something happens + * that would indicate a slowdown is needed, such as a failed request. + */ +ExponentialBackoff.prototype.increaseBackoff = function increaseBackoff() { + this.backoff *= this.exponent; +}; + +/** + * Decrease the backoff for the next request. The backoff will never go below the miniumum backoff + * value. This should be called when things are going smoothly, such as after a successful request. + */ +ExponentialBackoff.prototype.decreaseBackoff = function decreaseBackoff() { + if (this.backoff <= this.minimum_backoff) { + this.backoff = this.minimum_backoff; + } else { + this.backoff /= this.exponent; + } +}; + +module.exports = ExponentialBackoff; diff --git a/lib/run_tests.js b/lib/run_tests.js index 5e262bf..328126c 100644 --- a/lib/run_tests.js +++ b/lib/run_tests.js @@ -13,6 +13,7 @@ try { locations = []; } +var ExponentialBackoff = require( '../lib/ExponentialBackoff'); var util = require( 'util' ); var isObject = require( 'is-object' ); var request = require( 'request' ); @@ -158,6 +159,7 @@ function execTestSuite( apiUrl, testSuite, cb ){ return; } + var test_interval = new ExponentialBackoff(); testSuite.tests.forEach( function ( testCase ){ if( validTestStatuses.indexOf( testCase.status ) === -1 ){ throw util.format( @@ -204,6 +206,7 @@ function execTestSuite( apiUrl, testSuite, cb ){ return; } else if( retry_codes.indexOf(res.statusCode) !== -1 ){ + test_interval.increaseBackoff(); testSuite.tests.push( testCase ); return; } @@ -221,6 +224,8 @@ function execTestSuite( apiUrl, testSuite, cb ){ process.exit( 1 ); } + test_interval.decreaseBackoff(); + stats.testsCompleted++; process.stderr.write( util.format( '\rTests completed: %s/%s', stats.testsCompleted.toString().bold, @@ -269,7 +274,7 @@ function execTestSuite( apiUrl, testSuite, cb ){ cb( testResults ); } }); - }, 50); + }, test_interval.getBackoff()); } var stats = { diff --git a/test/ExponentialBackoff.js b/test/ExponentialBackoff.js new file mode 100644 index 0000000..6b8534c --- /dev/null +++ b/test/ExponentialBackoff.js @@ -0,0 +1,33 @@ + +var tape = require( 'tape' ); +var ExponentialBackoff = require( '../lib/ExponentialBackoff' ); + + +tape( 'ExponentialBackoff', function(test) { + test.test( 'calling increaseBackoff() makes backoff larger', function(t) { + var eb = new ExponentialBackoff(); + var startBackoff = eb.getBackoff(); + eb.increaseBackoff(); + t.ok(startBackoff < eb.getBackoff(), 'backoff was not higher'); + t.end(); + }); + + test.test( 'calling decreaseBackoff() lowers backoff', function(t) { + var eb = new ExponentialBackoff(); + eb.increaseBackoff(); + var startBackoff = eb.getBackoff(); + eb.decreaseBackoff(); + t.ok(startBackoff > eb.getBackoff(), 'backoff was not lower'); + t.end(); + }); + + test.test( 'calling decreaseBackoff() will not lower backoff below minimum', function(t) { + var eb = new ExponentialBackoff(); + var startBackoff = eb.getBackoff(); + eb.decreaseBackoff(); + t.ok(startBackoff === eb.getBackoff(), 'backoff was not equal'); + t.end(); + }); + + test.end(); +}); diff --git a/test/test.js b/test/test.js index 55a7ec6..428be5f 100644 --- a/test/test.js +++ b/test/test.js @@ -5,3 +5,4 @@ 'use strict'; require('./fuzzy-tester'); +require('./ExponentialBackoff');