Skip to content

Commit

Permalink
Fix:Elevated limits quota on params (#66)
Browse files Browse the repository at this point in the history
* fix:pass quota config through params instead of bucket config

* chore:adjusted readme

* chore:fixed resolveElevatedParams tests

* chore:fixed validateERLParams tests
  • Loading branch information
pubalokta authored May 20, 2024
1 parent e927bc4 commit b0d52ba
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 191 deletions.
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,6 @@ buckets = {
elevated_limits: { // Optional. ERL configuration if needed for the bucket. If not defined, the bucket will not use ERL.
size: 100, // Optional. New bucket size. already used tokens will be deducted from current bucket content upon ERL activation. Default: same as the original bucket.
per_second: 50, // Optional. New bucket refill rate. You can use all the other refill rate configurations defined above, such as per_minute, per_hour, per_interval etc. Default: same as the original bucket.
erl_activation_period_seconds: 300, // Mandatory. For how long the ERL configuration should remain active once activated. No default value.
quota_per_calendar_month: 100, // Mandatory. The amount of ERL activations that can be done in a calendar month. Each activation will remain active during erl_activation_period_seconds.
}
}
}
Expand Down Expand Up @@ -199,6 +197,8 @@ limitd.takeElevated(type, key, { count, configOverride, elevated_limits }, (err,
- `elevated_limits`: (object)
- `erl_is_active_key`: (string) the identifier of the ERL activation for the bucket.
- `erl_quota_key`: (string) the identifier of the ERL quota bucket name.
- `erl_activation_period_seconds`: (int) the ERL activation period as defined in the bucket configuration used in the current request.
- `quota_per_calendar_month`: (int) the amount of ERL activations that can be done in a calendar month. Each activation will remain active during `erl_activation_period_seconds`.

`quota_per_calendar_month` is the only refill rate available for ERL quota buckets at the moment.
The quota bucket will be used to track the amount of ERL activations that can be done in a calendar month.
Expand Down Expand Up @@ -257,11 +257,11 @@ const configOverride = {
// take one
limitd.take(type, key, { configOverride }, (err, result) => {
console.log(result);
}
});
// take multiple
limitd.take(type, key, { count: 3, configOverride }, (err, result) => {
console.log(result);
}););
});
```

Config overrides follow the same rules as Bucket configuration elements with respect to default size when not provided and ttl.
Expand All @@ -278,8 +278,6 @@ const configOverride = {
elevated_limits: {
size: 100,
per_hour: 50,
erl_activation_period_seconds: 300,
quota_per_calendar_month: 100
}
}
```
Expand Down
28 changes: 11 additions & 17 deletions lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const utils = require('./utils');
const Redis = require('ioredis');
const { validateParams, validateERLParams } = require('./validation');
const DBPing = require("./db_ping");
const { calculateQuotaExpiration } = require('./utils');
const { calculateQuotaExpiration, resolveElevatedParams } = require('./utils');
const EventEmitter = require('events').EventEmitter;

const TAKE_LUA = fs.readFileSync(`${__dirname}/take.lua`, "utf8");
Expand Down Expand Up @@ -290,26 +290,20 @@ class LimitDBRedis extends EventEmitter {
}

takeElevated(params, callback) {
const valError = validateERLParams(params.elevated_limits);
if (valError) {
return callback(valError)
let erlParams;

if (params.elevated_limits) {
erlParams = utils.getERLParams(params.elevated_limits);
const valError = validateERLParams(erlParams);
if (valError) {
return callback(valError)
}
}
const erlParams = utils.getERLParams(params.elevated_limits);

this._doTake(params, callback, (key, bucketKeyConfig, count) => {
// provide default values for elevated_limits unless the bucketKeyConfig has them
const elevated_limits = {
ms_per_interval: bucketKeyConfig.ms_per_interval,
size: bucketKeyConfig.size,
erl_activation_period_seconds: 900,
erl_quota: 0,
erl_quota_interval: 'quota_per_calendar_month',
erl_configured_for_bucket: false,
...bucketKeyConfig.elevated_limits
};

const elevated_limits = resolveElevatedParams(erlParams, bucketKeyConfig);
const erl_quota_expiration = calculateQuotaExpiration(elevated_limits);
this.redis.takeElevated(key, erlParams.erl_is_active_key, erlParams.erl_quota_key,
this.redis.takeElevated(key, elevated_limits.erl_is_active_key, elevated_limits.erl_quota_key,
bucketKeyConfig.ms_per_interval || 0,
bucketKeyConfig.size,
count,
Expand Down
54 changes: 33 additions & 21 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,28 +52,12 @@ function normalizeTemporals(params) {
}

function normalizeElevatedTemporals(params) {
if (!params.erl_activation_period_seconds) {
throw new LimitdRedisConfigurationError('erl_activation_period_seconds is required for elevated limits', {code: 201});
}

let type = normalizeTemporals(params);
type.erl_activation_period_seconds = params.erl_activation_period_seconds;

// extract erl quota information
ERL_QUOTA_INTERVALS_SHORTCUTS.forEach(intervalShortcut => {
if (!(intervalShortcut in params)) {
return;
}
type.erl_quota = params[intervalShortcut];
type.erl_quota_interval = intervalShortcut;
});

if (!type.erl_quota_interval || type.erl_quota === undefined) {
throw new LimitdRedisConfigurationError('a valid quota amount per interval is required for elevated limits', {code: 202});
if (typeof type.size !== 'undefined' && typeof type.per_interval !== 'undefined') {
type.erl_configured_for_bucket = true;
}

type.erl_configured_for_bucket = true;

return type;
}

Expand Down Expand Up @@ -149,10 +133,21 @@ function randomBetween(min, max) {
}

function getERLParams(params) {
return _.pick(params, [
const type = _.pick(params, [
'erl_is_active_key',
'erl_quota_key'
'erl_quota_key',
'erl_activation_period_seconds',
]);

// extract erl quota information
ERL_QUOTA_INTERVALS_SHORTCUTS.forEach(intervalShortcut => {
if (!(intervalShortcut in params)) {
return;
}
type.erl_quota = params[intervalShortcut];
type.erl_quota_interval = intervalShortcut;
});
return type;
}

function calculateQuotaExpiration(params) {
Expand All @@ -164,6 +159,22 @@ function endOfMonthTimestamp() {
return Date.UTC(curDate.getUTCFullYear(), curDate.getUTCMonth() + 1, 1, 0, 0, 0, 0);
}

function resolveElevatedParams(erlParams, bucketKeyConfig) {
// provide default values for elevated_limits unless the bucketKeyConfig has them
return {
ms_per_interval: bucketKeyConfig.ms_per_interval,
size: bucketKeyConfig.size,
erl_activation_period_seconds: 0,
erl_quota: 0,
erl_quota_interval: 'quota_per_calendar_month',
erl_is_active_key: 'defaultActiveKey',
erl_quota_key: 'defaultQuotaKey',
...erlParams,
...bucketKeyConfig.elevated_limits,
erl_configured_for_bucket: !!(erlParams && bucketKeyConfig.elevated_limits?.erl_configured_for_bucket),
};
}

class LimitdRedisConfigurationError extends Error {
constructor(msg, extra) {
super();
Expand All @@ -187,5 +198,6 @@ module.exports = {
randomBetween,
getERLParams,
endOfMonthTimestamp,
calculateQuotaExpiration
calculateQuotaExpiration,
resolveElevatedParams
};
9 changes: 9 additions & 0 deletions lib/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ function validateERLParams(params) {
if (typeof params.erl_quota_key !== 'string') {
return new LimitdRedisValidationError('erl_quota_key is required for elevated limits', { code: 110 });
}

if (typeof params.erl_activation_period_seconds !== 'number') {
return new LimitdRedisValidationError('erl_activation_period_seconds is required for elevated limits', { code: 111 });
}

if (typeof params.erl_quota !== 'number' || typeof params.erl_quota_interval !== 'string') {
return new LimitdRedisValidationError('a valid quota amount per interval is required for elevated limits', { code: 112 });
}

}

module.exports = {
Expand Down
Loading

0 comments on commit b0d52ba

Please sign in to comment.