forked from avinassh/ratelimit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtoken_bucket.lua
More file actions
61 lines (54 loc) · 1.7 KB
/
token_bucket.lua
File metadata and controls
61 lines (54 loc) · 1.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
local key = KEYS[1]
local rate = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local default_expiry = math.floor(window/1000 * 3)
-- https://stackoverflow.com/a/1252776
local function is_empty(table)
local next = next
if next(table) == nil then
return true
end
return false
end
-- https://stackoverflow.com/a/34313599
local function hgetall(hash_key)
local flat_map = redis.call("HGETALL", hash_key)
local result = {}
for i = 1, #flat_map, 2 do
result[flat_map[i]] = flat_map[i + 1]
end
return result
end
-- we would have stored as {ts: <timestamp>, c: <counter>}
local value = hgetall(key)
local function set(ts, counter)
redis.call("HMSET", key, "ts", ts, "c", counter)
redis.call("EXPIRE", key, default_expiry)
return {"ts", ts, "c", counter, "s", 1}
end
local function existing_counter(last_refill, counter)
redis.debug("checking if", counter, rate)
if counter < rate then
-- return redis.error_reply("counter rate")
return set(last_refill, counter + 1)
end
-- current limit has exceeded, lets check if it can be refiled
redis.debug("checking limit", last_refill+window, now)
if last_refill + window <= now then
-- return redis.error_reply("text")
return set(now, 1)
end
-- current limit has exceeded, but not refill either. just return the values
return {"ts", last_refill, "c", counter, "s", 0}
end
local function run()
if is_empty(value) then
return set(now, 1)
else
local last_refill = tonumber(value.ts) or 0
local counter = tonumber(value.c) or 0
return existing_counter(last_refill, counter)
end
end
return run()