-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathtoken_bucket.js
80 lines (72 loc) · 2.53 KB
/
token_bucket.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
module.exports = TokenBucket
/**
* An implementation of the Token Bucket algorithm.
*
* Basically, in network throttling, there are two "mainstream"
* algorithms for throttling requests, Token Bucket and Leaky Bucket.
* For restify, I went with Token Bucket. For a good description of the
* algorithm, see: http://en.wikipedia.org/wiki/Token_bucket
*
* In the options object, you pass in the total tokens and the fill rate.
* Practically speaking, this means "allow `fill rate` requests/(throttle window),
* with bursts up to `total tokens`". Note that the bucket is initialized
* to full.
*
* Also, in googling, I came across a concise python implementation, so this
* is just a port of that. Thanks http://code.activestate.com/recipes/511490 !
*
* @param {Object} options contains the parameters:
* - {Number} capacity the maximum burst.
* - {Number} fillRate the rate to refill tokens.
* - {Number} window the time window to consider for throttling
*/
function TokenBucket(options) {
if (!(this instanceof TokenBucket)) return new TokenBucket(options)
this.capacity = +options.capacity
this.tokens = +options.tokens
if (Number.isNaN(this.tokens)) {
this.tokens = this.capacity
}
this.fillRate = +options.fillRate
this.window = +options.window
this.time = +options.time || Date.now()
}
/**
* Consume N tokens from the bucket.
*
* If there is not capacity, the tokens are not pulled from the bucket.
*
* @param {Number} tokens the number of tokens to pull out.
* @return {Boolean} true if capacity, false otherwise.
*/
TokenBucket.prototype.consume = function consume(tokens) {
if (tokens <= this._fill()) {
this.tokens -= tokens
return true
}
return false
}
/**
* Fills the bucket with more tokens.
*
* Rather than do some whacky setTimeout() deal, we just approximate refilling
* the bucket by tracking elapsed time from the last time we touched the bucket.
*
* Simply, we set the bucket size to min(totalTokens,
* current + (fillRate * elapsed time)).
*
* @return {Number} the current number of tokens in the bucket.
*/
TokenBucket.prototype._fill = function _fill() {
var now = Date.now()
// reset account for clock drift (like DST)
if (now < this.time) {
this.time = now - this.window
}
if (this.tokens < this.capacity) {
var delta = (this.fillRate / this.window) * (now - this.time)
this.tokens = Math.min(this.capacity, this.tokens + delta)
}
this.time = now
return this.tokens
}