web-gatekeeper-js 1.0.10 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
export const slidingWindowScript = `
|
|
2
|
-
local key
|
|
3
|
-
local windowSize
|
|
4
|
-
local limit
|
|
5
|
-
local now
|
|
6
|
-
local ttl
|
|
2
|
+
local key = KEYS[1]
|
|
3
|
+
local windowSize = math.floor(tonumber(ARGV[1]))
|
|
4
|
+
local limit = math.floor(tonumber(ARGV[2]))
|
|
5
|
+
local now = math.floor(tonumber(ARGV[3]))
|
|
6
|
+
local ttl = math.floor(tonumber(ARGV[4]))
|
|
7
7
|
|
|
8
8
|
local windowStart = math.floor(now / windowSize) * windowSize
|
|
9
|
+
local resetAfter = math.ceil((windowSize - (now - windowStart)) / 1000)
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
local
|
|
12
|
-
local previousCount = tonumber(redis.call('HGET', key, 'previousCount') or 0)
|
|
13
|
-
|
|
14
|
-
-- resetAfter calculated once, used in all returns
|
|
15
|
-
local resetAfter = math.ceil((windowSize - (now - windowStart)) / 1000)
|
|
11
|
+
-- check BEFORE reading
|
|
12
|
+
local rawWindowStart = redis.call('HGET', key, 'windowStart')
|
|
16
13
|
|
|
17
14
|
-- first time user
|
|
18
|
-
if
|
|
15
|
+
if not rawWindowStart then
|
|
19
16
|
redis.call('HSET', key,
|
|
20
17
|
'windowStart', windowStart,
|
|
21
18
|
'currentCount', 1,
|
|
22
19
|
'previousCount', 0
|
|
23
20
|
)
|
|
24
|
-
redis.call('
|
|
25
|
-
return { 1, 1, limit - 1, resetAfter }
|
|
21
|
+
redis.call('EXPIRE', key, ttl)
|
|
22
|
+
return { 1, 1, limit - 1, resetAfter }
|
|
26
23
|
end
|
|
27
24
|
|
|
25
|
+
local storedWindowStart = tonumber(rawWindowStart)
|
|
26
|
+
local currentCount = tonumber(redis.call('HGET', key, 'currentCount'))
|
|
27
|
+
local previousCount = tonumber(redis.call('HGET', key, 'previousCount'))
|
|
28
|
+
|
|
28
29
|
local windowsPassed = math.floor((windowStart - storedWindowStart) / windowSize)
|
|
29
30
|
|
|
30
31
|
local newPreviousCount = previousCount
|
|
@@ -42,12 +43,10 @@ export const slidingWindowScript = `
|
|
|
42
43
|
local overlap = 1 - (elapsedTime / (windowSize / 1000))
|
|
43
44
|
local effectiveCount = (overlap * newPreviousCount) + newCurrentCount
|
|
44
45
|
|
|
45
|
-
-- blocked
|
|
46
46
|
if effectiveCount >= limit then
|
|
47
|
-
return { 0, newCurrentCount, 0, resetAfter }
|
|
47
|
+
return { 0, newCurrentCount, 0, resetAfter }
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
-- allowed
|
|
51
50
|
newCurrentCount = newCurrentCount + 1
|
|
52
51
|
|
|
53
52
|
redis.call('HSET', key,
|
|
@@ -55,8 +54,8 @@ export const slidingWindowScript = `
|
|
|
55
54
|
'currentCount', newCurrentCount,
|
|
56
55
|
'previousCount', newPreviousCount
|
|
57
56
|
)
|
|
58
|
-
redis.call('
|
|
57
|
+
redis.call('EXPIRE', key, ttl)
|
|
59
58
|
|
|
60
59
|
local remaining = math.floor(limit - effectiveCount - 1)
|
|
61
|
-
return { 1, newCurrentCount, remaining, resetAfter }
|
|
60
|
+
return { 1, newCurrentCount, remaining, resetAfter }
|
|
62
61
|
`
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export const throttlerScript = `
|
|
2
2
|
local key = KEYS[1]
|
|
3
|
-
local now = tonumber(ARGV[1])
|
|
3
|
+
local now = math.floor(tonumber(ARGV[1]))
|
|
4
4
|
local refillRate = tonumber(ARGV[2])
|
|
5
|
-
local maxWait = tonumber(ARGV[3])
|
|
6
|
-
local ttl = tonumber(ARGV[4])
|
|
5
|
+
local maxWait = math.floor(tonumber(ARGV[3]))
|
|
6
|
+
local ttl = math.floor(tonumber(ARGV[4]))
|
|
7
7
|
|
|
8
8
|
-- safely read nextAllowedTime
|
|
9
9
|
local raw = redis.call('HGET', key, 'nextAllowedTime')
|
|
@@ -14,21 +14,21 @@ export const throttlerScript = `
|
|
|
14
14
|
|
|
15
15
|
-- reject if queue too full
|
|
16
16
|
if waitTime > maxWait then
|
|
17
|
-
return { 0, waitTime }
|
|
17
|
+
return { 0, math.floor(waitTime) }
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
-- calculate new nextAllowedTime
|
|
21
21
|
local newNextAllowedTime
|
|
22
22
|
if waitTime <= 0 then
|
|
23
|
-
newNextAllowedTime = now + (1000 / refillRate)
|
|
23
|
+
newNextAllowedTime = math.floor(now + (1000 / refillRate))
|
|
24
24
|
else
|
|
25
|
-
newNextAllowedTime = nextAllowedTime + (1000 / refillRate)
|
|
25
|
+
newNextAllowedTime = math.floor(nextAllowedTime + (1000 / refillRate))
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
-- save and set TTL
|
|
29
29
|
redis.call('HSET', key, 'nextAllowedTime', newNextAllowedTime)
|
|
30
|
-
redis.call('
|
|
30
|
+
redis.call('EXPIRE', key, math.floor(ttl))
|
|
31
31
|
|
|
32
32
|
-- return allowed + waitTime so Node.js knows how long to delay
|
|
33
|
-
return { 1, math.max(waitTime, 0) }
|
|
33
|
+
return { 1, math.floor(math.max(waitTime, 0)) }
|
|
34
34
|
`
|
|
@@ -1,46 +1,41 @@
|
|
|
1
1
|
export const tokenBucketScript = `
|
|
2
2
|
local key = KEYS[1]
|
|
3
|
-
local now = tonumber(ARGV[1])
|
|
3
|
+
local now = math.floor(tonumber(ARGV[1]))
|
|
4
4
|
local maxToken = tonumber(ARGV[2])
|
|
5
5
|
local refillRate = tonumber(ARGV[3])
|
|
6
|
-
local ttl = tonumber(ARGV[4])
|
|
6
|
+
local ttl = math.floor(tonumber(ARGV[4]))
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
local
|
|
10
|
-
local tokenLeft = tonumber(redis.call('HGET', key, 'tokenLeft') or maxToken)
|
|
8
|
+
local rawTime = redis.call('HGET', key, 'time')
|
|
9
|
+
local rawTokens = redis.call('HGET', key, 'tokenLeft')
|
|
11
10
|
|
|
12
|
-
-- first time user
|
|
13
|
-
if
|
|
11
|
+
-- first time user
|
|
12
|
+
if not rawTime then
|
|
14
13
|
redis.call('HSET', key,
|
|
15
14
|
'time', now,
|
|
16
15
|
'tokenLeft', maxToken - 1
|
|
17
16
|
)
|
|
18
|
-
redis.call('
|
|
19
|
-
return { 1, maxToken - 1 }
|
|
17
|
+
redis.call('EXPIRE', key, ttl)
|
|
18
|
+
return { 1, maxToken - 1, 0 }
|
|
20
19
|
end
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
local
|
|
24
|
-
local updatedToken = math.min(tokenLeft + (timeElapsed * refillRate), maxToken)
|
|
21
|
+
local lastReqTime = tonumber(rawTime)
|
|
22
|
+
local tokenLeft = tonumber(rawTokens)
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
redis.call('HSET', key,
|
|
29
|
-
'time', now,
|
|
30
|
-
'tokenLeft', updatedToken
|
|
31
|
-
)
|
|
32
|
-
redis.call('PEXPIRE', key, math.floor(ttl))
|
|
24
|
+
local timeElapsed = (now - lastReqTime) / 1000
|
|
25
|
+
local updatedToken = math.min(tokenLeft + (timeElapsed * refillRate), maxToken)
|
|
33
26
|
|
|
27
|
+
if updatedToken < 1 then
|
|
28
|
+
redis.call('HSET', key, 'time', now, 'tokenLeft', updatedToken)
|
|
29
|
+
redis.call('EXPIRE', key, ttl)
|
|
34
30
|
local retryAfter = math.ceil((1 - updatedToken) / refillRate)
|
|
35
31
|
return { 0, 0, retryAfter }
|
|
36
32
|
end
|
|
37
33
|
|
|
38
|
-
-- allow — consume 1 token and save (same as your hSet)
|
|
39
34
|
redis.call('HSET', key,
|
|
40
35
|
'time', now,
|
|
41
36
|
'tokenLeft', updatedToken - 1
|
|
42
37
|
)
|
|
43
|
-
redis.call('
|
|
38
|
+
redis.call('EXPIRE', key, ttl)
|
|
44
39
|
|
|
45
|
-
return { 1, updatedToken - 1, 0 }
|
|
40
|
+
return { 1, math.floor(updatedToken - 1), 0 }
|
|
46
41
|
`
|