jhurliman / node-rate-limiter

A generic rate limiter for node.js. Useful for API clients, web crawling, or other tasks that need to be throttled
MIT License
1.5k stars 132 forks source link

Add tokens or dynamically set number of tokens #37

Closed justinffs closed 6 years ago

justinffs commented 7 years ago

I am using limiter to throttle calls to a DynamoDB table. DynamoDB will return the Consumed Capacity, which sometimes is 0. I would like to NOT delay the next call if the Consumed Capacity is 0 but I don't see a straight forward way to do this. I have tried setting then number of tokens to 2, then running tryRemoveTokens(1) if Consumed Capacity is > 0. I also tried the opposite and this.limiter.tokensThisInterval+=1; if Consumed Capacity is 0. Neither seems to work the way I thought it would. tryRemoveTokens would run into if (count > this.tokenBucket.tokensPerInterval - this.tokensThisInterval) and return false.

I am probably misunderstanding how this works. Here is example code snippets.

limiter = new RateLimiter(2, 1000);
limiter.removeTokens(2, (err, remainingRequests) => {
   query.returnConsumedCapacity().exec((err, items) => {
        if (items.ConsumedCapacity > 0 || items.Count > 0) {
           limiter.tryRemoveTokens(1);
         }
    });
});
limiter = new RateLimiter(1, 1000);
limiter.removeTokens(1, (err, remainingRequests) => {
   query.returnConsumedCapacity().exec((err, items) => {
        if (items.ConsumedCapacity === 0 || items.Count === 0) {
            this.limiter.tokensThisInterval+=1;
         }
    });
});
jhurliman commented 6 years ago

When you get information back from an API about what the true capacity is at any given moment, I think the best option is to modify the number of tokens in the token bucket directly. Example:

this.limiter.tokenBucket.content = 2

This works fine for reducing the current number of tokens. The problem with adding more tokens is that any current pending calls to removeTokens will already have a timer scheduled and there is no support (yet) for canceling that timer and retrying. It would be possible to add a refreshPending() method but it's not something I have time to implement myself right now.

justinffs commented 6 years ago

Thanks @jhurliman