Skip to content
This repository was archived by the owner on Jun 4, 2025. It is now read-only.

Commit 59a7dbb

Browse files
committed
fixed window & small fixes
1 parent c9f64b5 commit 59a7dbb

File tree

5 files changed

+37
-15
lines changed

5 files changed

+37
-15
lines changed

lib/db.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ const TAKE_ELEVATED_LUA = fs.readFileSync(`${__dirname}/take_elevated.lua`, 'utf
1414
const TAKE_EXPONENTIAL_LUA = fs.readFileSync(`${__dirname}/take_exponential.lua`, 'utf8');
1515
const PUT_LUA = fs.readFileSync(`${__dirname}/put.lua`, 'utf8');
1616

17+
const BACKOFF_FACTOR_DEFAULT = 2;
18+
const MULTIPLE_UNIT_DEFAULT = 1000;
1719
const DEFAULT_KEEPALIVE = 10000; // Milliseconds
1820

1921
class LimitDBRedis extends EventEmitter {
@@ -270,31 +272,37 @@ class LimitDBRedis extends EventEmitter {
270272

271273
takeExponential(params, callback) {
272274
this._doTake(params, callback, (key, bucketKeyConfig, count) => {
275+
const useFixedWindow = isFixedWindowEnabled(bucketKeyConfig.fixed_window, params.fixed_window);
273276
this.redis.takeExponential(key,
274277
bucketKeyConfig.ms_per_interval || 0,
275278
bucketKeyConfig.size,
276279
count,
277280
Math.ceil(bucketKeyConfig.ttl || this.globalTTL),
278281
bucketKeyConfig.drip_interval || 0,
279-
bucketKeyConfig.backoff_factor || 2,
280-
bucketKeyConfig.multiple_unit || 1000,
282+
bucketKeyConfig.backoff_factor || BACKOFF_FACTOR_DEFAULT,
283+
bucketKeyConfig.multiple_unit || MULTIPLE_UNIT_DEFAULT,
284+
useFixedWindow ? bucketKeyConfig.interval : 0,
281285
(err, results) => {
282286
if (err) {
283287
return callback(err);
284288
}
285289
const remaining = parseInt(results[0], 10);
286290
const conformant = parseInt(results[1], 10) ? true : false;
291+
const current_timestamp_ms = parseInt(results[2], 10);
287292
const reset = parseInt(results[3], 10);
288293
const backoff_factor = parseInt(results[4], 10);
289294
const backoff_time = parseInt(results[5], 10);
295+
const next_token_ms = parseInt(results[6], 10);
290296
const res = {
291297
conformant,
292298
remaining,
293299
reset: Math.ceil(reset / 1000),
294300
limit: bucketKeyConfig.size,
295301
delayed: false,
296302
backoff_factor,
297-
backoff_time
303+
backoff_time,
304+
delta_reset_ms: Math.max(reset - current_timestamp_ms, 0),
305+
delta_backoff_time: next_token_ms - current_timestamp_ms,
298306
};
299307
if (bucketKeyConfig.skip_n_calls > 0) {
300308
this.callCounts.set(key, { res, count: 0 });

lib/take_exponential.lua

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ local ttl = tonumber(ARGV[4])
66
local drip_interval = tonumber(ARGV[5])
77
local backoff_factor = tonumber(ARGV[6])
88
local mult_unit = tonumber(ARGV[7])
9+
local fixed_window = tonumber(ARGV[8])
910

1011
local current_time = redis.call('TIME')
1112
local current_timestamp_ms = current_time[1] * 1000 + current_time[2] / 1000
@@ -18,20 +19,29 @@ end
1819

1920
-- calculate the time of next available token
2021
local last_token_ms = current[1] or 0
21-
local step = 0
22+
local remaining_tokens = 0
2223
if current[2] then
23-
step = tonumber(current[2])
24+
remaining_tokens = tonumber(current[2])
2425
else
25-
step = bucket_size
26+
remaining_tokens = bucket_size
2627
end
2728

28-
local backoff_step = bucket_size - step
29+
local backoff_step = bucket_size - remaining_tokens
2930
local backoff_time = math.ceil(backoff_factor ^ backoff_step) * mult_unit
3031
local next_token_ms = last_token_ms + backoff_time
3132
local is_passed_wait_time = current_timestamp_ms >= next_token_ms
3233

3334
if current[1] and tokens_per_ms then
3435
-- drip bucket
36+
37+
if fixed_window > 0 then
38+
-- fixed window for granting new tokens
39+
local interval_correction = (current_timestamp_ms - last_token_ms) % fixed_window
40+
current_timestamp_ms = current_timestamp_ms - interval_correction
41+
end
42+
43+
is_passed_wait_time = current_timestamp_ms >= next_token_ms
44+
3545
if not is_passed_wait_time then
3646
new_content = tonumber(current[2])
3747
last_token_ms = current[1]
@@ -63,8 +73,10 @@ redis.call('HMSET', KEYS[1],
6373
redis.call('EXPIRE', KEYS[1], ttl)
6474

6575
local reset_ms = 0
66-
if drip_interval > 0 then
76+
if fixed_window > 0 then
77+
reset_ms = current_timestamp_ms + fixed_window
78+
elseif drip_interval > 0 then
6779
reset_ms = math.ceil(current_timestamp_ms + (bucket_size - new_content) * drip_interval)
6880
end
6981

70-
return { new_content, enough_tokens, current_timestamp_ms, reset_ms, backoff_factor, backoff_time, backoff_step }
82+
return { new_content, enough_tokens, current_timestamp_ms, reset_ms, backoff_factor, backoff_time, next_token_ms }

lib/utils.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ const INTERVAL_TO_MS = {
1111

1212
const INTERVAL_SHORTCUTS = Object.keys(INTERVAL_TO_MS);
1313

14+
const EXPONENTIAL_BACKOFF_DEFAULTS = {
15+
backoff_factor: 0,
16+
multiple_unit: 1
17+
};
18+
1419
const ERL_QUOTA_INTERVAL_PER_CALENDAR_MONTH = 'quota_per_calendar_month';
1520
const ERL_QUOTA_INTERVALS = {
1621
[ERL_QUOTA_INTERVAL_PER_CALENDAR_MONTH]: () => endOfMonthTimestamp()
@@ -41,8 +46,8 @@ function normalizeTemporals(params) {
4146
}
4247

4348
if(type.exponential_backoff) {
44-
type.backoff_factor= type.exponential_backoff.backoff_factor || 0;
45-
type.multiple_unit = type.exponential_backoff.multiple_unit || 1;
49+
type.backoff_factor= type.exponential_backoff.backoff_factor || EXPONENTIAL_BACKOFF_DEFAULTS.backoff_factor;
50+
type.multiple_unit = type.exponential_backoff.multiple_unit || EXPONENTIAL_BACKOFF_DEFAULTS.multiple_unit;
4651
delete type.exponential_backoff;
4752
}
4853

test/db.tests.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ module.exports.tests = (clientCreator) => {
162162

163163
describe('#configurateBucketKey', () => {
164164
it('should add new bucket to existing configuration', () => {
165-
console.log("this one works....")
166165
db.configurateBucket('test', { size: 5 });
167166
assert.containsAllKeys(db.buckets, ['ip', 'test']);
168167
});

test/utils.tests.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ describe('utils', () => {
5555
expect(rest).to.deep.equal({
5656
backoff_factor: 5,
5757
multiple_unit: 6
58-
})
59-
60-
58+
});
6159
});
6260

6361
it('should return normalized bucket without ERL', () => {

0 commit comments

Comments
 (0)