-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy pathsingle.ts
162 lines (130 loc) · 5.43 KB
/
single.ts
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
export const fixedWindowLimitScript = `
local key = KEYS[1]
local window = ARGV[1]
local incrementBy = ARGV[2] -- increment rate per request at a given value, default is 1
local r = redis.call("INCRBY", key, incrementBy)
if r == tonumber(incrementBy) then
-- The first time this key is set, the value will be equal to incrementBy.
-- So we only need the expire command once
redis.call("PEXPIRE", key, window)
end
return r
`;
export const fixedWindowRemainingTokensScript = `
local key = KEYS[1]
local tokens = 0
local value = redis.call('GET', key)
if value then
tokens = value
end
return tokens
`;
export const slidingWindowLimitScript = `
local currentKey = KEYS[1] -- identifier including prefixes
local previousKey = KEYS[2] -- key of the previous bucket
local tokens = tonumber(ARGV[1]) -- tokens per window
local now = ARGV[2] -- current timestamp in milliseconds
local window = ARGV[3] -- interval in milliseconds
local incrementBy = ARGV[4] -- increment rate per request at a given value, default is 1
local requestsInCurrentWindow = redis.call("GET", currentKey)
if requestsInCurrentWindow == false then
requestsInCurrentWindow = 0
end
local requestsInPreviousWindow = redis.call("GET", previousKey)
if requestsInPreviousWindow == false then
requestsInPreviousWindow = 0
end
local percentageInCurrent = ( now % window ) / window
-- weighted requests to consider from the previous window
requestsInPreviousWindow = math.floor(( 1 - percentageInCurrent ) * requestsInPreviousWindow)
if requestsInPreviousWindow + requestsInCurrentWindow >= tokens then
return -1
end
local newValue = redis.call("INCRBY", currentKey, incrementBy)
if newValue == tonumber(incrementBy) then
-- The first time this key is set, the value will be equal to incrementBy.
-- So we only need the expire command once
redis.call("PEXPIRE", currentKey, window * 2 + 1000) -- Enough time to overlap with a new window + 1 second
end
return tokens - ( newValue + requestsInPreviousWindow )
`;
export const slidingWindowRemainingTokensScript = `
local currentKey = KEYS[1] -- identifier including prefixes
local previousKey = KEYS[2] -- key of the previous bucket
local now = ARGV[1] -- current timestamp in milliseconds
local window = ARGV[2] -- interval in milliseconds
local requestsInCurrentWindow = redis.call("GET", currentKey)
if requestsInCurrentWindow == false then
requestsInCurrentWindow = 0
end
local requestsInPreviousWindow = redis.call("GET", previousKey)
if requestsInPreviousWindow == false then
requestsInPreviousWindow = 0
end
local percentageInCurrent = ( now % window ) / window
-- weighted requests to consider from the previous window
requestsInPreviousWindow = math.floor(( 1 - percentageInCurrent ) * requestsInPreviousWindow)
return requestsInPreviousWindow + requestsInCurrentWindow
`;
export const tokenBucketLimitScript = `
local key = KEYS[1] -- identifier including prefixes
local maxTokens = tonumber(ARGV[1]) -- maximum number of tokens
local interval = tonumber(ARGV[2]) -- size of the window in milliseconds
local refillRate = tonumber(ARGV[3]) -- how many tokens are refilled after each interval
local now = tonumber(ARGV[4]) -- current timestamp in milliseconds
local incrementBy = tonumber(ARGV[5]) -- how many tokens to consume, default is 1
local bucket = redis.call("HMGET", key, "refilledAt", "tokens")
local refilledAt
local tokens
if bucket[1] == false then
refilledAt = now
tokens = maxTokens
else
refilledAt = tonumber(bucket[1])
tokens = tonumber(bucket[2])
end
if now >= refilledAt + interval then
local numRefills = math.floor((now - refilledAt) / interval)
tokens = math.min(maxTokens, tokens + numRefills * refillRate)
refilledAt = refilledAt + numRefills * interval
end
if tokens == 0 then
return {-1, refilledAt + interval}
end
local remaining = tokens - incrementBy
local expireAt = math.ceil(((maxTokens - remaining) / refillRate)) * interval
redis.call("HSET", key, "refilledAt", refilledAt, "tokens", remaining)
redis.call("PEXPIRE", key, expireAt)
return {remaining, refilledAt + interval}
`;
export const tokenBucketIdentifierNotFound = -1
export const tokenBucketRemainingTokensScript = `
local key = KEYS[1]
local maxTokens = tonumber(ARGV[1])
local bucket = redis.call("HMGET", key, "refilledAt", "tokens")
if bucket[1] == false then
return {maxTokens, ${tokenBucketIdentifierNotFound}}
end
return {tonumber(bucket[2]), tonumber(bucket[1])}
`;
export const cachedFixedWindowLimitScript = `
local key = KEYS[1]
local window = ARGV[1]
local incrementBy = ARGV[2] -- increment rate per request at a given value, default is 1
local r = redis.call("INCRBY", key, incrementBy)
if r == incrementBy then
-- The first time this key is set, the value will be equal to incrementBy.
-- So we only need the expire command once
redis.call("PEXPIRE", key, window)
end
return r
`;
export const cachedFixedWindowRemainingTokenScript = `
local key = KEYS[1]
local tokens = 0
local value = redis.call('GET', key)
if value then
tokens = value
end
return tokens
`;