@@ -21,15 +21,15 @@ local setmetatable = setmetatable
2121local tostring = tostring
2222
2323
24- local _M = {version = 0.3 }
24+ local _M = {version = 0.4 }
2525
2626
2727local mt = {
2828 __index = _M
2929}
3030
3131
32- local script = core .string .compress_script ([=[
32+ local script_fixed = core .string .compress_script ([=[
3333 assert(tonumber(ARGV[3]) >= 1, "cost must be at least 1")
3434 local ttl = redis.call('ttl', KEYS[1])
3535 if ttl < 0 then
@@ -40,12 +40,61 @@ local script = core.string.compress_script([=[
4040]=] )
4141
4242
43- function _M .new (plugin_name , limit , window , conf )
43+ local script_sliding = core .string .compress_script ([=[
44+ assert(tonumber(ARGV[3]) >= 1, "cost must be at least 1")
45+
46+ local now = tonumber(ARGV[1])
47+ local window = tonumber(ARGV[2])
48+ local limit = tonumber(ARGV[3])
49+ local cost = tonumber(ARGV[4])
50+
51+ local window_start = now - window
52+
53+ -- remove events outside of the window
54+ redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, window_start)
55+
56+ local current = redis.call('ZCARD', KEYS[1])
57+
58+ if current + cost > limit then
59+ local earliest = redis.call('ZRANGE', KEYS[1], 0, 0, 'WITHSCORES')
60+ local reset = 0
61+ if #earliest == 2 then
62+ reset = earliest[2] + window - now
63+ if reset < 0 then
64+ reset = 0
65+ end
66+ end
67+ return {-1, reset}
68+ end
69+
70+ for i = 1, cost do
71+ redis.call('ZADD', KEYS[1], now, now .. ':' .. i)
72+ end
73+
74+ redis.call('PEXPIRE', KEYS[1], window)
75+
76+ local remaining = limit - (current + cost)
77+
78+ local earliest = redis.call('ZRANGE', KEYS[1], 0, 0, 'WITHSCORES')
79+ local reset = 0
80+ if #earliest == 2 then
81+ reset = earliest[2] + window - now
82+ if reset < 0 then
83+ reset = 0
84+ end
85+ end
86+
87+ return {remaining, reset}
88+ ]=] )
89+
90+
91+ function _M .new (plugin_name , limit , window , window_type , conf )
4492 assert (limit > 0 and window > 0 )
4593
4694 local self = {
4795 limit = limit ,
4896 window = window ,
97+ window_type = window_type or " fixed" ,
4998 conf = conf ,
5099 plugin_name = plugin_name ,
51100 }
@@ -59,13 +108,22 @@ function _M.incoming(self, key, cost)
59108 return red , err , 0
60109 end
61110
62- local limit = self .limit
63- local window = self .window
64- local res
65111 key = self .plugin_name .. tostring (key )
66112
67113 local ttl = 0
68- res , err = red :eval (script , 1 , key , limit , window , cost or 1 )
114+ local limit = self .limit
115+ local c = cost or 1
116+ local res
117+
118+ if self .window_type == " sliding" then
119+ local now = ngx .now () * 1000
120+ local window = self .window * 1000
121+
122+ res , err = red :eval (script_sliding , 1 , key , now , window , limit , c )
123+ else
124+ local window = self .window
125+ res , err = red :eval (script_fixed , 1 , key , limit , window , c )
126+ end
69127
70128 if err then
71129 return nil , err , ttl
@@ -74,14 +132,15 @@ function _M.incoming(self, key, cost)
74132 local remaining = res [1 ]
75133 ttl = res [2 ]
76134
77- local ok , err = red :set_keepalive (10000 , 100 )
135+ local ok , err2 = red :set_keepalive (10000 , 100 )
78136 if not ok then
79- return nil , err , ttl
137+ return nil , err2 , ttl
80138 end
81139
82140 if remaining < 0 then
83141 return nil , " rejected" , ttl
84142 end
143+
85144 return 0 , remaining , ttl
86145end
87146
0 commit comments