falvaradorodriguez commented on code in PR #12605:
URL: https://github.com/apache/apisix/pull/12605#discussion_r2736393769
##########
apisix/plugins/limit-req/util.lua:
##########
@@ -14,61 +14,97 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-local math = require "math"
-local abs = math.abs
-local max = math.max
local ngx_now = ngx.now
-local ngx_null = ngx.null
local tonumber = tonumber
+local tostring = tostring
+local type = type
+local core = require("apisix.core")
+local resty_string = require("resty.string")
local _M = {version = 0.1}
+local script = core.string.compress_script([=[
+ local state_key = KEYS[1] -- state_key (hash), fields:
"excess", "last"
+ local rate = tonumber(ARGV[1]) -- req/s
+ local now = tonumber(ARGV[2]) -- ms
+ local burst = tonumber(ARGV[3]) -- req/s
+ local commit = tonumber(ARGV[4]) -- 1/0
+
+ local vals = redis.call("HMGET", state_key, "excess", "last")
+ local prev_excess = tonumber(vals[1] or "0")
+ local prev_last = tonumber(vals[2] or "0")
+
+ local new_excess
+ if prev_last > 0 then
+ local elapsed = math.abs(now - prev_last)
+ new_excess = math.max(prev_excess - rate * (elapsed) / 1000 + 1000, 0)
+ else
+ new_excess = 0
+ end
+
+ if new_excess > burst then
+ return {0, new_excess}
+ end
+
+ if commit == 1 then
+ redis.call("HMSET", state_key, "excess", new_excess, "last", now)
+ local ttl = math.ceil(burst / rate) + 1
+ redis.call("EXPIRE", state_key, ttl)
+ end
+
+ return {1, new_excess}
+]=])
+
+-- Pre-calculate SHA1 for EVALSHA optimization
+local script_sha1 = resty_string.to_hex(ngx.sha1_bin(script))
+
+
+local function is_noscript_error(err)
+ if not err then
+ return false
+ end
+
+ local s
+ if type(err) == "table" then
+ s = tostring(err[1] or err.err or err.message or err.msg or err)
+ else
+ s = tostring(err)
+ end
+
+ return s:find("NOSCRIPT", 1, true) ~= nil
+end
+
+
-- the "commit" argument controls whether should we record the event in shm.
function _M.incoming(self, red, key, commit)
local rate = self.rate
local now = ngx_now() * 1000
- key = "limit_req" .. ":" .. key
- local excess_key = key .. "excess"
- local last_key = key .. "last"
+ local state_key = "limit_req:{" .. key .. "}:state"
- local excess, err = red:get(excess_key)
- if err then
- return nil, err
- end
- local last, err = red:get(last_key)
- if err then
- return nil, err
- end
+ local commit_flag = commit and "1" or "0"
- if excess ~= ngx_null and last ~= ngx_null then
- excess = tonumber(excess)
- last = tonumber(last)
- local elapsed = now - last
- excess = max(excess - rate * abs(elapsed) / 1000 + 1000, 0)
+ -- Try EVALSHA first (fast path).
+ local res, err = red:evalsha(script_sha1, 1, state_key,
+ rate, now, self.burst, commit_flag)
- if excess > self.burst then
- return nil, "rejected"
- end
- else
- excess = 0
+ -- If the script isn't cached on this Redis node, fall back to EVAL.
+ if not res and is_noscript_error(err) then
Review Comment:
Makes sense, and it is also good practice to keep the logic of the plugins
aligned. I have tried to replicate the functionality you mention here:
https://github.com/apache/apisix/pull/12605/changes/05657f67151416b4af9bd37937bc3b76e9fa5b27
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]