This is an automated email from the ASF dual-hosted git repository.

nic-6443 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new 06c15d327 perf(limit-count): use evalsha with NOSCRIPT fallback for 
Redis script execution (#13363)
06c15d327 is described below

commit 06c15d3271a9d744332197d41e084519b7955429
Author: Nic <[email protected]>
AuthorDate: Thu May 14 16:09:01 2026 +0800

    perf(limit-count): use evalsha with NOSCRIPT fallback for Redis script 
execution (#13363)
---
 .../limit-count/limit-count-redis-cluster.lua      |  8 ++-
 apisix/plugins/limit-count/limit-count-redis.lua   |  8 ++-
 t/plugin/limit-count-redis.t                       | 62 +++++++++++++++++-----
 3 files changed, 62 insertions(+), 16 deletions(-)

diff --git a/apisix/plugins/limit-count/limit-count-redis-cluster.lua 
b/apisix/plugins/limit-count/limit-count-redis-cluster.lua
index be7029b66..89a823e61 100644
--- a/apisix/plugins/limit-count/limit-count-redis-cluster.lua
+++ b/apisix/plugins/limit-count/limit-count-redis-cluster.lua
@@ -17,6 +17,7 @@
 
 local redis_cluster = require("apisix.utils.rediscluster")
 local core = require("apisix.core")
+local to_hex = require("resty.string").to_hex
 local setmetatable = setmetatable
 local tostring = tostring
 
@@ -37,6 +38,7 @@ local script = core.string.compress_script([=[
     end
     return {redis.call('incrby', KEYS[1], 0 - ARGV[3]), ttl}
 ]=])
+local script_sha = to_hex(ngx.sha1_bin(script))
 
 
 function _M.new(plugin_name, limit, window, conf)
@@ -64,7 +66,11 @@ function _M.incoming(self, key, cost)
     key = self.plugin_name .. tostring(key)
 
     local ttl = 0
-    local res, err = red:eval(script, 1, key, limit, window, cost or 1)
+    local res, err = red:evalsha(script_sha, 1, key, limit, window, cost or 1)
+    if err and core.string.has_prefix(err, "NOSCRIPT") then
+        core.log.warn("redis evalsha failed: ", err, ". Falling back to eval")
+        res, err = red:eval(script, 1, key, limit, window, cost or 1)
+    end
 
     if err then
         return nil, err, ttl
diff --git a/apisix/plugins/limit-count/limit-count-redis.lua 
b/apisix/plugins/limit-count/limit-count-redis.lua
index ce78383fe..5580b22a0 100644
--- a/apisix/plugins/limit-count/limit-count-redis.lua
+++ b/apisix/plugins/limit-count/limit-count-redis.lua
@@ -16,6 +16,7 @@
 --
 local redis     = require("apisix.utils.redis")
 local core = require("apisix.core")
+local to_hex = require("resty.string").to_hex
 local assert = assert
 local setmetatable = setmetatable
 local tostring = tostring
@@ -38,6 +39,7 @@ local script = core.string.compress_script([=[
     end
     return {redis.call('incrby', KEYS[1], 0 - ARGV[3]), ttl}
 ]=])
+local script_sha = to_hex(ngx.sha1_bin(script))
 
 
 function _M.new(plugin_name, limit, window, conf)
@@ -65,7 +67,11 @@ function _M.incoming(self, key, cost)
     key = self.plugin_name .. tostring(key)
 
     local ttl = 0
-    res, err = red:eval(script, 1, key, limit, window, cost or 1)
+    res, err = red:evalsha(script_sha, 1, key, limit, window, cost or 1)
+    if err and core.string.has_prefix(err, "NOSCRIPT") then
+        core.log.warn("redis evalsha failed: ", err, ". Falling back to eval")
+        res, err = red:eval(script, 1, key, limit, window, cost or 1)
+    end
 
     if err then
         return nil, err, ttl
diff --git a/t/plugin/limit-count-redis.t b/t/plugin/limit-count-redis.t
index 3adeb054a..47d529932 100644
--- a/t/plugin/limit-count-redis.t
+++ b/t/plugin/limit-count-redis.t
@@ -195,7 +195,41 @@ GET /hello
 
 
 
-=== TEST 7: set route, with redis host, port and right password
+=== TEST 7: flush redis script cache
+--- config
+    location /t {
+        content_by_lua_block {
+            local redis = require("resty.redis")
+            local red = redis:new()
+            red:set_timeout(1000)
+            local ok, err = red:connect("127.0.0.1", 6379)
+            if not ok then
+                ngx.say("failed to connect: ", err)
+                return
+            end
+            red:script("FLUSH")
+            red:flushall()
+            red:set_keepalive(10000, 100)
+            ngx.say("done")
+        }
+    }
+--- response_body
+done
+
+
+
+=== TEST 8: evalsha NOSCRIPT fallback after SCRIPT FLUSH
+--- request
+GET /hello
+--- error_code: 200
+--- grep_error_log eval
+qr/redis evalsha failed:.*Falling back to eval/
+--- grep_error_log_out
+redis evalsha failed: NOSCRIPT No matching script. Please use EVAL.. Falling 
back to eval
+
+
+
+=== TEST 9: set route, with redis host, port and right password
 --- config
     location /t {
         content_by_lua_block {
@@ -270,7 +304,7 @@ passed
 
 
 
-=== TEST 8: up the limit
+=== TEST 10: up the limit
 --- pipelined_requests eval
 ["GET /hello", "GET /hello", "GET /hello", "GET /hello"]
 --- error_code eval
@@ -278,7 +312,7 @@ passed
 
 
 
-=== TEST 9: up the limit
+=== TEST 11: up the limit
 --- pipelined_requests eval
 ["GET /hello1", "GET /hello", "GET /hello2", "GET /hello", "GET /hello"]
 --- error_code eval
@@ -286,7 +320,7 @@ passed
 
 
 
-=== TEST 10: set route, with redis host, port and wrong password
+=== TEST 12: set route, with redis host, port and wrong password
 --- config
     location /t {
         content_by_lua_block {
@@ -328,7 +362,7 @@ passed
 
 
 
-=== TEST 11: request for TEST 10
+=== TEST 13: request for TEST 10
 --- request
 GET /hello_new
 --- error_code eval
@@ -338,7 +372,7 @@ failed to limit count: WRONGPASS invalid username-password 
pair or user is disab
 
 
 
-=== TEST 12: multi request for TEST 10
+=== TEST 14: multi request for TEST 10
 --- pipelined_requests eval
 ["GET /hello_new", "GET /hello1", "GET /hello1", "GET /hello_new"]
 --- no_error_log
@@ -348,7 +382,7 @@ failed to limit count: WRONGPASS invalid username-password 
pair or user is disab
 
 
 
-=== TEST 13: set route, with redis host, port and bad username and good 
password
+=== TEST 15: set route, with redis host, port and bad username and good 
password
 --- config
     location /t {
         content_by_lua_block {
@@ -392,7 +426,7 @@ passed
 
 
 
-=== TEST 14: request for TEST 13
+=== TEST 16: request for TEST 13
 --- request
 GET /hello
 --- error_code eval
@@ -402,7 +436,7 @@ failed to limit count: WRONGPASS invalid username-password 
pair or user is disab
 
 
 
-=== TEST 15: set route, with redis host, port and good username and bad 
password
+=== TEST 17: set route, with redis host, port and good username and bad 
password
 --- config
     location /t {
         content_by_lua_block {
@@ -446,7 +480,7 @@ passed
 
 
 
-=== TEST 16: request for TEST 15
+=== TEST 18: request for TEST 15
 --- request
 GET /hello
 --- error_code eval
@@ -456,7 +490,7 @@ failed to limit count: WRONGPASS invalid username-password 
pair or user is disab
 
 
 
-=== TEST 17: set route, with redis host, port and right username and password
+=== TEST 19: set route, with redis host, port and right username and password
 --- config
     location /t {
         content_by_lua_block {
@@ -500,7 +534,7 @@ passed
 
 
 
-=== TEST 18: up the limit
+=== TEST 20: up the limit
 --- pipelined_requests eval
 ["GET /hello", "GET /hello", "GET /hello", "GET /hello"]
 --- error_code eval
@@ -508,7 +542,7 @@ passed
 
 
 
-=== TEST 19: up the limit
+=== TEST 21: up the limit
 --- pipelined_requests eval
 ["GET /hello1", "GET /hello", "GET /hello2", "GET /hello", "GET /hello"]
 --- error_code eval
@@ -516,7 +550,7 @@ passed
 
 
 
-=== TEST 20: restore redis password to ''
+=== TEST 22: restore redis password to ''
 --- config
     location /t {
         content_by_lua_block {

Reply via email to