-
Notifications
You must be signed in to change notification settings - Fork 0
/
limit.lua
70 lines (68 loc) · 2.18 KB
/
limit.lua
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
62
63
64
65
66
67
68
69
70
local function fixedWindow(key, value, size, limit)
local counter = redis.call("get", key)
if counter == false then
counter = 0
end
if counter + value > limit then
local ttl = redis.call("pttl", key)
if ttl == -2 then
ttl = 0
end
return { 0, tonumber(counter), ttl }
end
if counter == 0 then
redis.call("set", key, value, "px", size)
return { 1, tonumber(value), tonumber(size) }
end
return { 1, redis.call("incrby", key, value), redis.call("pttl", key) }
end
local function slidingWindow(key, value, size, limit)
local t = redis.call("time")
local now = t[1] * 1000 + math.floor(t[2]/1000)
local currWindowTime = now - now % size
local currWindowKey = key .. ":" .. currWindowTime
local prevWindowKey = key .. ":" .. currWindowTime - size
local currWindowCounter = redis.call("get", currWindowKey)
if currWindowCounter == false then
currWindowCounter = 0
end
local prevWindowCounter = redis.call("get", prevWindowKey)
if prevWindowCounter == false then
prevWindowCounter = 0
end
local currWindowRemainingDuration = size - (now - currWindowTime)
local slidingWindowCounter = math.floor(prevWindowCounter * (currWindowRemainingDuration / size) + currWindowCounter)
local counter = slidingWindowCounter + value
if counter > limit then
return { 0, slidingWindowCounter, currWindowRemainingDuration }
end
if currWindowCounter == 0 then
redis.call("set", currWindowKey, value, "px", size * 2)
else
redis.call("incrby", currWindowKey, value)
end
return { 1, counter, currWindowRemainingDuration }
end
local z = 0
local limit, v, result
for i, key in ipairs(KEYS) do
z = z + 4
limit = tonumber(ARGV[z - 1])
if ARGV[z] == "1" then
v = fixedWindow(key, ARGV[z - 3], ARGV[z - 2], limit)
else
v = slidingWindow(key, ARGV[z - 3], ARGV[z - 2], limit)
end
if i == 1 then -- first result
result = { v[1], v[2], v[3], limit }
elseif v[1] == 1 then -- ok
if result[1] == 1 and result[4] - result[2] > limit - v[2] then -- minimal remainder
result = { v[1], v[2], v[3], limit }
end
elseif result[1] == 1 then -- not ok first time
result = { v[1], v[2], v[3], limit }
elseif result[3] < v[3] then -- maximum TTL
result = { v[1], v[2], v[3], limit }
end
end
return result