fluxguard 0.1.1

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.
@@ -0,0 +1,22 @@
1
+ -- KEYS[1]=prev KEYS[2]=curr
2
+ -- ARGV: limit, window_ms, now_ms
3
+ local limit = tonumber(ARGV[1])
4
+ local window = tonumber(ARGV[2])
5
+ local now = tonumber(ARGV[3])
6
+ local prev = tonumber(redis.call('GET', KEYS[1])) or 0
7
+ local curr = tonumber(redis.call('GET', KEYS[2])) or 0
8
+ local elapsed = now % window
9
+ local weight = 1 - (elapsed / window)
10
+ local estimated = math.floor(prev * weight) + curr
11
+ if estimated >= limit then
12
+ local currStart = now - elapsed
13
+ local reset = currStart + window
14
+ return {0, estimated, limit, reset, reset - now}
15
+ end
16
+ redis.call('INCR', KEYS[2])
17
+ redis.call('PEXPIRE', KEYS[2], window * 2)
18
+ redis.call('PEXPIRE', KEYS[1], window * 2)
19
+ local nextEst = estimated + 1
20
+ local currStart = now - elapsed
21
+ local reset = currStart + window
22
+ return {1, limit - nextEst, limit, reset, 0}
@@ -0,0 +1,20 @@
1
+ -- KEYS[1] = zset key
2
+ -- ARGV: limit, window_ms, now_ms, member unique id
3
+ local limit = tonumber(ARGV[1])
4
+ local window = tonumber(ARGV[2])
5
+ local now = tonumber(ARGV[3])
6
+ local member = ARGV[4]
7
+ local cutoff = now - window
8
+ redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', cutoff)
9
+ local count = redis.call('ZCARD', KEYS[1])
10
+ if count >= limit then
11
+ local oldest = redis.call('ZRANGE', KEYS[1], 0, 0, 'WITHSCORES')
12
+ local ts = tonumber(oldest[2])
13
+ local reset = ts + window
14
+ return {0, 0, limit, reset, reset - now}
15
+ end
16
+ redis.call('ZADD', KEYS[1], now, member)
17
+ redis.call('PEXPIRE', KEYS[1], window * 2)
18
+ local newCount = count + 1
19
+ local reset = now + window
20
+ return {1, limit - newCount, limit, reset, 0}
@@ -0,0 +1,26 @@
1
+ -- KEYS[1]=tokens KEYS[2]=last_refill
2
+ -- ARGV: limit(capacity), window_ms, now_ms
3
+ local capacity = tonumber(ARGV[1])
4
+ local window = tonumber(ARGV[2])
5
+ local now = tonumber(ARGV[3])
6
+ local refill = capacity / window
7
+ local tokens = tonumber(redis.call('GET', KEYS[1]))
8
+ local last = tonumber(redis.call('GET', KEYS[2]))
9
+ if not tokens then tokens = capacity end
10
+ if not last then last = now end
11
+ local delta = math.max(0, now - last)
12
+ tokens = math.min(capacity, tokens + delta * refill)
13
+ if tokens < 1 then
14
+ local need = 1 - tokens
15
+ local retry = math.ceil(need / refill)
16
+ local full = math.ceil(capacity / refill)
17
+ return {0, 0, capacity, now + full, retry}
18
+ end
19
+ tokens = tokens - 1
20
+ redis.call('SET', KEYS[1], tostring(tokens))
21
+ redis.call('SET', KEYS[2], tostring(now))
22
+ redis.call('PEXPIRE', KEYS[1], window * 3)
23
+ redis.call('PEXPIRE', KEYS[2], window * 3)
24
+ local remaining = math.floor(tokens)
25
+ local full = math.ceil((capacity - tokens) / refill)
26
+ return {1, remaining, capacity, now + full, 0}