Skip to content

Commit

Permalink
#62 RateLimiterQueue wrap many internal instances to remove tokens by…
Browse files Browse the repository at this point in the history
… custom key
  • Loading branch information
animirr committed Apr 18, 2020
1 parent b5cfcf8 commit 46647d3
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 4 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ Some copy/paste examples on Wiki:
* [RLWrapperBlackAndWhite](https://github.com/animir/node-rate-limiter-flexible/wiki/Black-and-White-lists) Black and White lists
* [RateLimiterQueue](https://github.com/animir/node-rate-limiter-flexible/wiki/RateLimiterQueue) Rate limiter with FIFO queue

### Changelog

See [releases](https://github.com/animir/node-rate-limiter-flexible/releases) for detailed changelog.

## Basic Options

* **points**
Expand Down Expand Up @@ -159,7 +163,7 @@ Some copy/paste examples on Wiki:
* [tableName](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#tablename) Table/collection.
* [clearExpiredByTimeout](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#clearexpiredbytimeout) For MySQL and PostgreSQL.

Cut off load picks:
Smooth out traffic picks:
* [execEvenly](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#execevenly)
* [execEvenlyMinDelayMs](https://github.com/animir/node-rate-limiter-flexible/wiki/Options#execevenlymindelayms)

Expand Down Expand Up @@ -218,3 +222,5 @@ It has to implement at least 4 methods:
* `_delete` deletes all key related data and returns `true` on deleted, `false` if key is not found.

All other methods depends on store. See `RateLimiterRedis` or `RateLimiterPostgres` for example.

Note: all changes should be covered by tests.
38 changes: 36 additions & 2 deletions lib/RateLimiterQueue.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,46 @@
const RateLimiterQueueError = require('./component/RateLimiterQueueError')
const MAX_QUEUE_SIZE = 4294967295;
const KEY_DEFAULT = 'limiter';

module.exports = class RateLimiterQueue {
constructor(limiterFlexible, opts = {
maxQueueSize: MAX_QUEUE_SIZE,
}) {
this._queueLimiters = {
KEY_DEFAULT: new RateLimiterQueueInternal(limiterFlexible, opts)
};
this._limiterFlexible = limiterFlexible;
this._maxQueueSize = opts.maxQueueSize
}

getTokensRemaining(key = KEY_DEFAULT) {
if (this._queueLimiters[key]) {
return this._queueLimiters[key].getTokensRemaining()
} else {
return Promise.resolve(this._limiterFlexible.points)
}
}

removeTokens(tokens, key = KEY_DEFAULT) {
if (!this._queueLimiters[key]) {
this._queueLimiters[key] = new RateLimiterQueueInternal(
this._limiterFlexible, {
key,
maxQueueSize: this._maxQueueSize,
})
}

return this._queueLimiters[key].removeTokens(tokens)
}
};

class RateLimiterQueueInternal {

constructor(limiterFlexible, opts = {
maxQueueSize: MAX_QUEUE_SIZE,
key: KEY_DEFAULT,
}) {
this._key = 'limiter';
this._key = opts.key;
this._waitTimeout = null;
this._queue = [];
this._limiterFlexible = limiterFlexible;
Expand Down Expand Up @@ -90,4 +124,4 @@ module.exports = class RateLimiterQueue {
}
});
}
};
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rate-limiter-flexible",
"version": "2.1.3",
"version": "2.1.4",
"description": "Node.js rate limiter by key and protection from DDoS and Brute-Force attacks in process Memory, Redis, MongoDb, Memcached, MySQL, PostgreSQL, Cluster or PM",
"main": "index.js",
"scripts": {
Expand Down
24 changes: 24 additions & 0 deletions test/RateLimiterQueue.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,28 @@ describe('RateLimiterQueue with FIFO queue', function RateLimiterQueueTest() {
});
});
});

it('getTokensRemaining returns maximum if internal limiter by key does not exist', (done) => {
const rlMemory = new RateLimiterMemory({ points: 23, duration: 1 });
const rlQueue = new RateLimiterQueue(rlMemory);
rlQueue.getTokensRemaining('test')
.then((tokensRemaining) => {
expect(tokensRemaining).to.equal(23);
done();
});
});

it('creates internal instance by key and removes tokens from it', (done) => {
const rlMemory = new RateLimiterMemory({ points: 2, duration: 1 });
const rlQueue = new RateLimiterQueue(rlMemory);
rlQueue.removeTokens(1, 'customkey')
.then((remainingTokens) => {
expect(remainingTokens).to.equal(1);
rlQueue.getTokensRemaining()
.then((defaultTokensRemaining) => {
expect(defaultTokensRemaining).to.equal(2);
done();
});
});
});
});

0 comments on commit 46647d3

Please sign in to comment.