Comments (3)
Looks like using TokenBucket
itself gets the results I expect:
const { TokenBucket } = require('limiter')
const bucket = new TokenBucket(10, 10, 'minute', null)
const remove = () => {
let date = new Date()
let remaining
if (bucket.tryRemoveTokens(1)) {
remaining = bucket.content
console.log(`${date} - token removed, remaining: ${remaining}`)
} else {
console.log(`${date} - NO tokens remaining`)
}
}
bucket.content = 10
remove()
setInterval(() => {
remove()
}, 3500)
Output:
Sat May 04 2019 18:00:35 GMT-0400 (EDT) - token removed, remaining: 9
Sat May 04 2019 18:00:39 GMT-0400 (EDT) - token removed, remaining: 8.5845
Sat May 04 2019 18:00:42 GMT-0400 (EDT) - token removed, remaining: 8.168666666666667
Sat May 04 2019 18:00:46 GMT-0400 (EDT) - token removed, remaining: 7.7524999999999995
Sat May 04 2019 18:00:49 GMT-0400 (EDT) - token removed, remaining: 7.336499999999999
Sat May 04 2019 18:00:53 GMT-0400 (EDT) - token removed, remaining: 6.920499999999999
Sat May 04 2019 18:00:56 GMT-0400 (EDT) - token removed, remaining: 6.504499999999998
Sat May 04 2019 18:01:00 GMT-0400 (EDT) - token removed, remaining: 6.088499999999998
[SNIP]
Sat May 04 2019 18:01:49 GMT-0400 (EDT) - token removed, remaining: 0.26333333333332964
Sat May 04 2019 18:01:52 GMT-0400 (EDT) - NO tokens remaining
Sat May 04 2019 18:01:56 GMT-0400 (EDT) - token removed, remaining: 0.43133333333332957
Tokens appear to be getting dripped back into the bucket over the course of the interval vs. being dumped into the bucket all at once at the beginning of the next interval (as when using RateLimiter
).
from node-rate-limiter.
@brenc Were you ever able to resolve this issue? I am having the same issue and finding the package unusable because of it.
from node-rate-limiter.
I ended up creating my own TokenBucket
class, something like this:
'use strict'
const assert = require('assert').strict
const debug = require('debug')('TokenBucket')
const allowedIntervals = [
'second',
'minute',
'hour',
'day'
]
const intervalError = `parameter "interval" must be the number of ` +
`milliseconds or one of the following: ${allowedIntervals.join(', ')}`
class TokenBucket {
constructor ({
interval,
size
}) {
assert(['string', 'number'].includes(typeof interval), intervalError)
if (typeof interval === 'number') {
assert(interval > 0, 'parameter "interval" must be > 0')
}
assert(typeof size === 'number', 'parameter "size" must be a number')
assert(size > 0, 'parameter "size" must be > 0')
this._lastDrip = +new Date()
this._size = size
// Set the initial number of tokens in the bucket to the size of the
// bucket.
this._tokens = size
if (typeof interval === 'string') {
switch (interval) {
case 'second':
this._interval = 1000
break
case 'minute':
this._interval = 1000 * 60
break
case 'hour':
this._interval = 1000 * 60 * 60
break
case 'day':
this._interval = 1000 * 60 * 60 * 24
break
default:
throw new Error(intervalError)
}
} else {
this._interval = interval
}
debug(`size: ${this._size}, tokens: ${this._tokens}, interval: ` +
`${this._interval}`)
}
_drip () {
const now = +new Date()
const delta = Math.max(now - this._lastDrip, 0)
const dripNum = delta * (this._size / this._interval)
this._lastDrip = now
debug(`ms since last drip: ${delta}, number of tokens going back into ` +
`the bucket: ${dripNum}, current number of tokens in the bucket: ` +
`${this._tokens}`)
// This prevents the bucket from "overflowing."
this._tokens = Math.min(this._tokens + dripNum, this._size)
debug(`number of tokens after the drip: ${this._tokens}`)
}
remaining () {
this._drip()
return this._tokens
}
remove (num) {
assert(typeof num === 'number', 'parameter "num" must be a number')
assert(num > 0, 'parameter "num" must be > 0')
if (num > this._size) {
debug(`removing ${num} tokens from the bucket`)
return false
}
this._drip()
// No more tokens left in the bucket.
if (num > this._tokens) {
debug(`bucket is empty`)
return false
}
this._tokens -= num
return true
}
size () {
return this._size
}
}
module.exports = TokenBucket
Here's how it's used:
// Limit to 60/minute
const bucket = new TokenBucket({
interval: 'minute',
size: 60
})
if (bucket.remove(1)) { // returns false if no more tokens left
doSomethingRateLimited()
} else {
sendRateLimitedMessage()
}
console.log(`there are ${bucket.remaining()} tokens remaining`)
This has worked well for several years. There are probably better solutions esp. if you need to scale beyond one process.
from node-rate-limiter.
Related Issues (20)
- Would this project be open to a PR adding asyncRemoveTokens? HOT 2
- Is it possible to use async function as callback? HOT 1
- removeTokens 1st parameter documentation HOT 1
- How to use rate limiter and retrieve my business method result HOT 1
- 'process' is not defined HOT 1
- Error [ERR_MODULE_NOT_FOUND]: Cannot find module '.../node_modules/limiter/dist/esm/RateLimiter' HOT 6
- remove "type": "module" in `package.json` HOT 3
- Unexpected token '?' HOT 5
- no Changelog HOT 3
- Got ERR_REQUIRE_ESM HOT 3
- Importing from esm in Node.js is broken in v2.1.0 HOT 23
- Tiered Rate Limiting - Extending TokenBucket to support such functionality.
- [Feature Request] Add a way to await "previous consumed tokens"
- Restore TokenBucket/RateLimiter state from localStorage
- RateLimiter slowing down with thousand of calls
- Throttlers and rate limiters, how they should be implemented HOT 1
- No way to calculate amount of time until the next drip?
- Wait from final request of the interval rather than the first
- Use global.performance instead of perf_hooks.performance for Node >16
- API client on multiple servers
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from node-rate-limiter.