-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Getting a negative number for expiration #11
Comments
Thanks @thoop |
Thanks for the quick response! |
I tried to write a test that reproduce this issue, and it passed.
any thoughts? |
Let me see if I can reproduce it. Are you actually using redis in your test or do you have it mocked out? |
actual redis. the tests are in test directory, you can run them by executing |
Sorry I haven't gotten to this yet. I've been swamped lately. I did run those tests and everything looked good so I want to look into this more once I have a chance! |
I modified the test and got it to fail reliably around the edge case of the seconds/milliseconds. In the last test of
You can see the while loop I added. It basically causes the test to wait until it's going to be on the cusp of a rounding issue. It looks like the rounding is causing redis to cleanup the counts before the millisecond resolution. We're seeing issues with this when running on AWS with Elasticache because we get millisecond response times. By removing the rounding, it looks to fix that issue...but it breaks a few other tests because now it's giving milliseconds back from the calls and not seconds. Thoughts? |
Hi thoop, On Wed, Sep 16, 2015, 10:06 Todd Hooper [email protected] wrote:
|
I'll look into this soon. I don't think just changing redis to fakeredis would make it fail for the same reason unless fakeredis handles expiration differently. |
Can you check please if #13 solves your issue? |
Sure, thanks! I'll report back. |
closing due to inactivity |
I am able to recreate this with the latest version from npm (3.3.1).
const Limiter = require('ratelimiter')
const rateLimiters = {}
const delay = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms))
const rateLimit = async (bucketName, clientId) => {
const bucketDefinition = bucketDefinitions[bucketName]
if (!bucketDefinition) {
throw new Error(`Bucket definition not found: ${bucketName}`)
}
const key = `${bucketName}:${clientId}`
if (!rateLimiters[key]) {
rateLimiters[key] = new Limiter({
db: cacheModule.getConnection(), // redis, not ioredis or mock-redis
id: key,
max: bucketDefinition.max,
duration: bucketDefinition.duration
})
}
const limit = rateLimiters[key]
const delta = await new Promise((resolve, reject) => {
limit.get((err, rate) => {
if (err) {
return reject(err)
}
if (rate.remaining) {
return resolve(0)
}
let delta = (rate.reset * 1000) - Date.now() | 0
// watch out for weird negative delta problem
if (delta <= 0) {
delta = Math.ceil(bucketDefinition.duration / bucketDefinition.max)
}
logger.log({
level: 'debug',
message: 'Rate limited',
key,
rate,
delta
})
return resolve(delta)
})
})
if (delta) {
await delay(delta)
return rateLimit(bucketName, clientId)
}
} |
just to make sure I understand - the |
does that help you, or do you need me to log |
can you use the resetMs instead? |
https://github.com/tj/node-ratelimiter#example That code is from your example. I switched to a different package, thanks! |
I'm getting a negative number for expiration causing my redis client to come back with this error:
Line 105 uses this:
where my ex is
1441268386
and Date.now() was1441268386507
meaning it was -507ms over expiration.The problem here is that error is in a weird double array so the error check doesn't catch it and my responses to
limit.get(function(err, limit) { ... });
always shown
remaining so it effectively doesn't rate limit at all.I assume other people would have run into this. I was using a duration of 1 second and I'm hitting redis pretty hard concurrently.
Is this line stripping off the milliseconds and just making it accurate to the second?
The text was updated successfully, but these errors were encountered: