SGrondin / bottleneck

Job scheduler and rate limiter, supports Clustering
MIT License
1.82k stars 75 forks source link

Support rate limiting with sliding window log algorithm #128

Open stansv opened 5 years ago

stansv commented 5 years ago

As far as I know the sliding window log technique gives the best accuracy having only one disadvantage in relatively high memory consumption, which is OK for API clients. We consume API with strict limits: 60 requests per one-minute window and 5000 per 24-hour window. Exceeding these limits is very undesirable since each new attempt will be counted by server and fail, extending the time we cannot use API.

Do you have any plans to support such functionality? Looks like this can be accomplished with ZREMRANGEBYSCORE (https://engineering.classdojo.com/blog/2015/02/06/rolling-rate-limiter/). There's also some NodeJS implementation referenced from this article but looks like it doesn't support delayed execution as like in your library.

UnbrandedTech commented 4 years ago

Thank you for the ticket, was just looking to implement something like this. Currently trying to wrap Hubspot's API calls. Hubspot uses a window limit instead of a hard limit like stripe.

SleepWalker commented 4 years ago

@stansv how about chaining the limiters?

const limiterPerDay = new Bottleneck({
  reservoir: 5000,
  reservoirRefreshAmount: 5000,
  reservoirRefreshInterval: 24 * 60 * 60 * 1000, // must be divisible by 250
});
const limiterPerMinute = new Bottleneck({
  reservoir: 60,
  reservoirRefreshAmount: 60,
  reservoirRefreshInterval: 60 * 1000, // must be divisible by 250
});
limiterPerMinute.chain(limiterPerDay);

limiterPerMinute.schedule(() => {...});

To handle out of limit errors you can use the following code:


limiterPerMinute.on('failed', (error, jobInfo) => {
  const isOutOfLimits = ...;

  if (isOutOfLimits) {
    limiterPerMinute.incrementReservoir(-Infinity);

    return 60 * 1000;
  }
});