From 11a8bee1ea03d06afba9621cefe1fe3ab74a02f9 Mon Sep 17 00:00:00 2001 From: SGrondin Date: Sat, 26 Jul 2014 13:09:40 -0400 Subject: [PATCH] 1.5.1 Fix small changeSettings bug --- README.md | 8 +++++++- bottleneck.js | 7 ++++--- bottleneck.min.js | 2 +- bower.json | 2 +- lib/Bottleneck.js | 1 + package.json | 2 +- src/Bottleneck.coffee | 4 +++- 7 files changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6a5f482..426af64 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ And now you can be assured that someAsyncCall will abide by your rate guidelines Bottleneck builds a queue of requests and executes them as soon as possible. All the requests will be executed *in order*. -This is sufficient for the vast majority of applications. Read the [Gotchas](https://github.com/SGrondin/bottleneck#gotchas) section and you're good to go. Or keep reading to learn about the fine tuning available for the more complex cases. +This is sufficient for the vast majority of applications. Read the [Gotchas](https://github.com/SGrondin/bottleneck#gotchas) section and you're good to go. Or keep reading to learn about all the fine tuning available for the more complex cases. #Docs @@ -116,6 +116,12 @@ limiter.changeSettings(maxConcurrent, minTime, highWater, strategy); ``` Same parameters as the constructor, pass ```null``` to skip a parameter and keep it to its current value. +**Note:** Changing `maxConcurrent` and `minTime` will not affect requests that have already been scheduled for execution. + +For example, imagine that 3 minute-long requests are `submit`'ted at time T+0 with `maxConcurrent = 0` and `minTime = 2000`. The requests will be launched at T+0 seconds, T+2 seconds and T+4 seconds respectively. If right after adding the requests to Bottleneck, you were to call `limiter.changeSettings(1);`, it won't change the fact that there will be 3 requests running at the same time for roughly 60 seconds as in this example they each take a minute to complete. Once again, `changeSettings` only affects requests that have not yet been `submit`'ted. + +This is by design, as Bottleneck made a promise to execute those requests according to the settings valid at the time. Changing settings afterwards should not retroactively affect space & time nor break previous assumptions as that would make code very error-prone and Bottleneck a tool that cannot be relied upon. + ###changePenalty() ```javascript diff --git a/bottleneck.js b/bottleneck.js index fafb1f6..bc576e9 100644 --- a/bottleneck.js +++ b/bottleneck.js @@ -1,4 +1,4 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0)};Bottleneck.prototype._tryToRun=function(){var done,index,next,wait;if((this._nbRunning0&&(this.reservoir==null||this.reservoir>0)){this._nbRunning++;if(this.reservoir!=null){this.reservoir--}wait=Math.max(this._nextRequest-Date.now(),0);this._nextRequest=Date.now()+wait+this.minTime;next=this._queue.shift();done=false;index=-1+this._timeouts.push(setTimeout(function(_this){return function(){return next.task.apply({},next.args.concat(function(){var _ref;if(!done){done=true;delete _this._timeouts[index];_this._nbRunning--;_this._tryToRun();if(!_this.interrupt){return(_ref=next.cb)!=null?_ref.apply({},Array.prototype.slice.call(arguments,0)):void 0}}}))}}(this),wait));return true}else{return false}};Bottleneck.prototype.submit=function(){var args,cb,reachedHighWaterMark,task,_i;task=arguments[0],args=3<=arguments.length?__slice.call(arguments,1,_i=arguments.length-1):(_i=1,[]),cb=arguments[_i++];reachedHighWaterMark=this.highWater>0&&this._queue.length===this.highWater;if(this.strategy===Bottleneck.prototype.strategy.BLOCK&&(reachedHighWaterMark||this._unblockTime>=Date.now())){this._unblockTime=Date.now()+this.penalty;this._nextRequest=this._unblockTime+this.minTime;return true}else if(reachedHighWaterMark){if(this.strategy===Bottleneck.prototype.strategy.LEAK){this._queue.shift()}else if(this.strategy===Bottleneck.prototype.strategy.OVERFLOW){return reachedHighWaterMark}}this._queue.push({task:task,args:args,cb:cb});this._tryToRun();return reachedHighWaterMark};Bottleneck.prototype.changeSettings=function(maxNb,minTime,highWater,strategy){this.maxNb=maxNb!=null?maxNb:this.maxNb;this.minTime=minTime!=null?minTime:this.minTime;this.highWater=highWater!=null?highWater:this.highWater;this.strategy=strategy!=null?strategy:this.strategy;return this};Bottleneck.prototype.changePenalty=function(penalty){this.penalty=penalty!=null?penalty:this.penalty;return this};Bottleneck.prototype.changeReservoir=function(reservoir){this.reservoir=reservoir;while(this._tryToRun()){}return this};Bottleneck.prototype.incrementReservoir=function(incr){if(incr==null){incr=0}this.changeReservoir(this.reservoir+incr);return this};Bottleneck.prototype.stopAll=function(interrupt){var a,_i,_len,_ref;this.interrupt=interrupt!=null?interrupt:this.interrupt;_ref=this._timeouts;for(_i=0,_len=_ref.length;_i<_len;_i++){a=_ref[_i];clearTimeout(a)}this._tryToRun=function(){};this.submit=function(){return false};return this.check=function(){return false}};return Bottleneck}();module.exports=Bottleneck}).call(this)},{}],2:[function(require,module,exports){(function(global){(function(){module.exports=require("./Bottleneck");if(global.window!=null){global.window.Bottleneck=module.exports}}).call(this)}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./Bottleneck":1}]},{},[2]); \ No newline at end of file +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0)};Bottleneck.prototype._tryToRun=function(){var done,index,next,wait;if((this._nbRunning0&&(this.reservoir==null||this.reservoir>0)){this._nbRunning++;if(this.reservoir!=null){this.reservoir--}wait=Math.max(this._nextRequest-Date.now(),0);this._nextRequest=Date.now()+wait+this.minTime;next=this._queue.shift();done=false;index=-1+this._timeouts.push(setTimeout(function(_this){return function(){return next.task.apply({},next.args.concat(function(){var _ref;if(!done){done=true;delete _this._timeouts[index];_this._nbRunning--;_this._tryToRun();if(!_this.interrupt){return(_ref=next.cb)!=null?_ref.apply({},Array.prototype.slice.call(arguments,0)):void 0}}}))}}(this),wait));return true}else{return false}};Bottleneck.prototype.submit=function(){var args,cb,reachedHighWaterMark,task,_i;task=arguments[0],args=3<=arguments.length?__slice.call(arguments,1,_i=arguments.length-1):(_i=1,[]),cb=arguments[_i++];reachedHighWaterMark=this.highWater>0&&this._queue.length===this.highWater;if(this.strategy===Bottleneck.prototype.strategy.BLOCK&&(reachedHighWaterMark||this._unblockTime>=Date.now())){this._unblockTime=Date.now()+this.penalty;this._nextRequest=this._unblockTime+this.minTime;return true}else if(reachedHighWaterMark){if(this.strategy===Bottleneck.prototype.strategy.LEAK){this._queue.shift()}else if(this.strategy===Bottleneck.prototype.strategy.OVERFLOW){return reachedHighWaterMark}}this._queue.push({task:task,args:args,cb:cb});this._tryToRun();return reachedHighWaterMark};Bottleneck.prototype.changeSettings=function(maxNb,minTime,highWater,strategy){this.maxNb=maxNb!=null?maxNb:this.maxNb;this.minTime=minTime!=null?minTime:this.minTime;this.highWater=highWater!=null?highWater:this.highWater;this.strategy=strategy!=null?strategy:this.strategy;while(this._tryToRun()){}return this};Bottleneck.prototype.changePenalty=function(penalty){this.penalty=penalty!=null?penalty:this.penalty;return this};Bottleneck.prototype.changeReservoir=function(reservoir){this.reservoir=reservoir;while(this._tryToRun()){}return this};Bottleneck.prototype.incrementReservoir=function(incr){if(incr==null){incr=0}this.changeReservoir(this.reservoir+incr);return this};Bottleneck.prototype.stopAll=function(interrupt){var a,_i,_len,_ref;this.interrupt=interrupt!=null?interrupt:this.interrupt;_ref=this._timeouts;for(_i=0,_len=_ref.length;_i<_len;_i++){a=_ref[_i];clearTimeout(a)}this._tryToRun=function(){};this.submit=function(){return false};return this.check=function(){return false}};return Bottleneck}();module.exports=Bottleneck}).call(this)},{}],2:[function(require,module,exports){(function(global){(function(){module.exports=require("./Bottleneck");if(global.window!=null){global.window.Bottleneck=module.exports}}).call(this)}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./Bottleneck":1}]},{},[2]); \ No newline at end of file diff --git a/bower.json b/bower.json index 10aab70..01003a5 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "bottleneck", "main": "bottleneck.js", - "version": "1.5.0", + "version": "1.5.1", "homepage": "https://github.com/SGrondin/bottleneck", "authors": [ "SGrondin " diff --git a/lib/Bottleneck.js b/lib/Bottleneck.js index 13dcf77..7275fe4 100644 --- a/lib/Bottleneck.js +++ b/lib/Bottleneck.js @@ -91,6 +91,7 @@ this.minTime = minTime != null ? minTime : this.minTime; this.highWater = highWater != null ? highWater : this.highWater; this.strategy = strategy != null ? strategy : this.strategy; + while (this._tryToRun()) {} return this; }; diff --git a/package.json b/package.json index c0815b8..7d8dc97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bottleneck", - "version": "1.5.0", + "version": "1.5.1", "description": "Async rate limiter", "main": "lib/index.js", "scripts": { diff --git a/src/Bottleneck.coffee b/src/Bottleneck.coffee index 4a886e2..03cd19c 100644 --- a/src/Bottleneck.coffee +++ b/src/Bottleneck.coffee @@ -41,7 +41,9 @@ class Bottleneck @_queue.push {task, args, cb} @_tryToRun() reachedHighWaterMark - changeSettings: (@maxNb=@maxNb, @minTime=@minTime, @highWater=@highWater, @strategy=@strategy) -> @ + changeSettings: (@maxNb=@maxNb, @minTime=@minTime, @highWater=@highWater, @strategy=@strategy) -> + while @_tryToRun() then + @ changePenalty: (@penalty=@penalty) -> @ changeReservoir: (@reservoir) -> while @_tryToRun() then