Skip to content

Commit

Permalink
1.4.0 check() and interrupt
Browse files Browse the repository at this point in the history
  • Loading branch information
SGrondin committed Jul 14, 2014
1 parent f424ad6 commit d44c514
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 20 deletions.
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ All the submitted requests will be executed *in order*.
#Docs

###Constructor
```var limiter = new Bottleneck(maxConcurrent, minTime, highWater, strategy);```
```javascript
var limiter = new Bottleneck(maxConcurrent, minTime, highWater, strategy);
```

* maxConcurrent : How many requests can be running at the same time. *Default: 0 (unlimited)*
* minTime : How long to wait after launching a request before launching another one. *Default: 0ms*
Expand Down Expand Up @@ -70,14 +72,22 @@ When submitting a new request, if the queue length reaches `highWater`, drop the
When submitting a new request, if the queue length reaches `highWater`, do not add the new request.

####Bottleneck.strategy.BLOCK
When submitting a new request, if the queue length reaches `highWater`, the limiter falls into "blocked mode". No new requests will be accepted until it unblocks. It will unblock after `penalty` milliseconds have passed without receiving a new request. `penalty` is equal to `8 * minTime` by default and can be changed by calling `changePenalty()`. This strategy is ideal when bruteforce attacks are to be expected.
When submitting a new request, if the queue length reaches `highWater`, the limiter falls into "blocked mode". No new requests will be accepted until it unblocks. It will unblock after `penalty` milliseconds have passed without receiving a new request. `penalty` is equal to `15 * minTime` (or 5000 if `minTime` is 0) by default and can be changed by calling `changePenalty()`. This strategy is ideal when bruteforce attacks are to be expected.


###check()
```javascript
limiter.check();
```
If a task was submitted right now, would it be run immediately? Returns a boolean.

###stopAll()
```javascript
limiter.stopAll();
limiter.stopAll(interrupt);
```
Cancels all queued up requests and prevents additonal requests from being submitted.
Cancels all *queued up* requests and prevents additonal requests from being submitted.

* interrupt : If true, prevent the tasks currently running from calling their callback when they're done. *Default: false*

###changeSettings()
```javascript
Expand Down
22 changes: 18 additions & 4 deletions bottleneck.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@
this._queue = [];
this._timeouts = [];
this._unblockTime = 0;
this.penalty = 8 * this.minTime;
this.penalty = (15 * this.minTime) || 5000;
this.interrupt = false;
}

Bottleneck.prototype.check = function() {
return (this._nbRunning < this.maxNb || this.maxNb <= 0) && (this._nextRequest - Date.now()) <= 0;
};

Bottleneck.prototype._tryToRun = function() {
var done, index, next, wait;
if ((this._nbRunning < this.maxNb || this.maxNb <= 0) && this._queue.length > 0) {
Expand All @@ -41,7 +46,9 @@
delete _this._timeouts[index];
_this._nbRunning--;
_this._tryToRun();
return (_ref = next.cb) != null ? _ref.apply({}, Array.prototype.slice.call(arguments, 0)) : void 0;
if (!_this.interrupt) {
return (_ref = next.cb) != null ? _ref.apply({}, Array.prototype.slice.call(arguments, 0)) : void 0;
}
}
}));
};
Expand All @@ -55,6 +62,7 @@
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) {
Expand Down Expand Up @@ -85,15 +93,21 @@
return this;
};

Bottleneck.prototype.stopAll = function() {
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() {};
return this.submit = function() {};
this.submit = function() {
return false;
};
return this.check = function() {
return false;
};
};

return Bottleneck;
Expand Down
2 changes: 1 addition & 1 deletion bottleneck.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 18 additions & 4 deletions lib/Bottleneck.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bottleneck",
"version": "1.3.1",
"version": "1.4.0",
"description": "Async rate limiter",
"main": "lib/index.js",
"scripts": {
Expand Down
16 changes: 10 additions & 6 deletions src/Bottleneck.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,30 @@ class Bottleneck
@_queue = []
@_timeouts = []
@_unblockTime = 0
@penalty = 8 * @minTime
@penalty = (15 * @minTime) or 5000
@interrupt = false
check: -> (@_nbRunning < @maxNb or @maxNb <= 0) and (@_nextRequest-Date.now()) <= 0
_tryToRun: ->
if (@_nbRunning < @maxNb or @maxNb <= 0) and @_queue.length > 0
@_nbRunning++
wait = Math.max @_nextRequest-Date.now(), 0
@_nextRequest = Date.now() + wait + @minTime
next = @_queue.shift()
done = false
index = -1 + @_timeouts.push setTimeout () =>
next.task.apply {}, next.args.concat () =>
index = -1 + @_timeouts.push setTimeout =>
next.task.apply {}, next.args.concat =>
if not done
done = true
delete @_timeouts[index]
@_nbRunning--
@_tryToRun()
next.cb?.apply {}, Array::slice.call arguments, 0
if not @interrupt then next.cb?.apply {}, Array::slice.call arguments, 0
, wait
submit: (task, args..., cb) ->
reachedHighWaterMark = @highWater > 0 and @_queue.length == @highWater
if @strategy == Bottleneck::strategy.BLOCK and (reachedHighWaterMark or @_unblockTime >= Date.now())
@_unblockTime = Date.now() + @penalty
@_nextRequest = @_unblockTime + @minTime
return true
else if reachedHighWaterMark
if @strategy == Bottleneck::strategy.LEAK then @_queue.shift()
Expand All @@ -36,9 +39,10 @@ class Bottleneck
reachedHighWaterMark
changeSettings: (@maxNb=@maxNb, @minTime=@minTime, @highWater=@highWater, @strategy=@strategy) -> @
changePenalty: (@penalty=@penalty) -> @
stopAll: ->
stopAll: (@interrupt=@interrupt) ->
(clearTimeout a for a in @_timeouts)
@_tryToRun = ->
@submit = ->
@submit = -> false
@check = -> false

module.exports = Bottleneck

0 comments on commit d44c514

Please sign in to comment.