This is an automated email from the ASF dual-hosted git repository.
AlinsRan 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 08aef845b fix(plugin): preserve plugin-conf state across consumer
route merge (#13757)
08aef845b is described below
commit 08aef845bda644ce9a19f2ef465dbc40b6ca233c
Author: AlinsRan <[email protected]>
AuthorDate: Wed Aug 5 06:35:33 2026 +0800
fix(plugin): preserve plugin-conf state across consumer route merge (#13757)
---
apisix/core/table.lua | 19 +++++++++++++++-
apisix/plugin.lua | 9 +++++++-
t/core/table.t | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/apisix/core/table.lua b/apisix/core/table.lua
index 60b0a6eba..97c37f295 100644
--- a/apisix/core/table.lua
+++ b/apisix/core/table.lua
@@ -31,6 +31,9 @@ local pairs = pairs
local type = type
local ngx_re = require("ngx.re")
local isarray = require("table.isarray")
+local str_byte = string.byte
+local str_has_prefix = require("apisix.core.string").has_prefix
+local DOT = str_byte(".")
local _M = {
@@ -120,6 +123,18 @@ end
local deepcopy
do
+ -- the members of `parent` are shallow-copied when `parent` is `prefix`
+ -- itself or one of its descendants. The match has to stop at a path
+ -- separator, otherwise a prefix like "self.value.plugins" would also
+ -- claim a sibling named "self.value.plugins2".
+ local function under_shallow_prefix(parent, prefix)
+ if not str_has_prefix(parent, prefix) then
+ return false
+ end
+
+ return #parent == #prefix or str_byte(parent, #prefix + 1) == DOT
+ end
+
local function _deepcopy(orig, copied, parent, opts)
-- If the array-like table contains nil in the middle,
-- the len might be smaller than the expected.
@@ -130,7 +145,9 @@ do
copied[orig] = copy
for orig_key, orig_value in pairs(orig) do
local path = parent .. "." .. tostring(orig_key)
- if opts and array_find(opts.shallows, path) then
+ if opts and (array_find(opts.shallows, path) or
+ (opts.shallow_prefix and
+ under_shallow_prefix(parent, opts.shallow_prefix))) then
copy[orig_key] = orig_value
else
if type(orig_value) == "table" then
diff --git a/apisix/plugin.lua b/apisix/plugin.lua
index 7a0159fd9..d00cc5310 100644
--- a/apisix/plugin.lua
+++ b/apisix/plugin.lua
@@ -818,7 +818,14 @@ local function merge_consumer_route(route_conf,
consumer_conf, consumer_group_co
return route_conf
end
- local new_route_conf = core.table.deepcopy(route_conf)
+ -- some plugins cache request-time state on their conf object (resolved DNS
+ -- nodes, probed backend versions, ...). Deep-copying the plugin confs
would
+ -- hand every consumer its own copy and drop that state, so keep them
shared
+ -- by reference, as they already are on the route without consumer auth.
The
+ -- `plugins` container itself is still a fresh table, so the merge below
+ -- overwrites its keys without touching the original route conf.
+ local new_route_conf = core.table.deepcopy(route_conf,
+ { shallow_prefix = "self.value.plugins" })
if has_group_plugins then
for name, conf in pairs(consumer_group_conf.value.plugins) do
diff --git a/t/core/table.t b/t/core/table.t
index 38616ae53..e1e4d6cfa 100644
--- a/t/core/table.t
+++ b/t/core/table.t
@@ -359,3 +359,65 @@ tab_copied.a.b.c == tab.a.b.c1: true
tab_copied.a.b.c == t1: true
tab_copied.x.y == tab.x.y: false
tab_copied.x.y == t2: false
+
+
+
+=== TEST 13: shallow_prefix
+--- config
+ location /t {
+ content_by_lua_block {
+ local core = require("apisix.core")
+ local deepcopy = core.table.deepcopy
+ local plugin1 = {name = "plugin1"}
+ local plugin2 = {name = "plugin2"}
+ local tab = {
+ plugins = {
+ p1 = plugin1,
+ p2 = plugin2,
+ },
+ }
+
+ local tab_copied = deepcopy(tab, { shallow_prefix = "self.plugins"
})
+
+ tab_copied.plugins.p1 = {name = "plugin1_new_modified"}
+ tab_copied.plugins.p2.name = "plugin2_modified"
+
+ ngx.say("table copied: ",
require("toolkit.json").encode(tab_copied))
+ ngx.say("table original: ", require("toolkit.json").encode(tab))
+ }
+ }
+--- request
+GET /t
+--- response_body
+table copied:
{"plugins":{"p1":{"name":"plugin1_new_modified"},"p2":{"name":"plugin2_modified"}}}
+table original:
{"plugins":{"p1":{"name":"plugin1"},"p2":{"name":"plugin2_modified"}}}
+--- no_error_log
+[error]
+
+
+
+=== TEST 14: shallow_prefix only matches at a path separator
+--- config
+ location /t {
+ content_by_lua_block {
+ local core = require("apisix.core")
+ local deepcopy = core.table.deepcopy
+ local tab = {
+ plugins = { p1 = {name = "p1"} },
+ -- a sibling whose name starts with the prefix
+ plugins2 = { p2 = {name = "p2"} },
+ }
+
+ local tab_copied = deepcopy(tab, { shallow_prefix = "self.plugins"
})
+
+ ngx.say("plugins.p1 shared: ", tab_copied.plugins.p1 ==
tab.plugins.p1)
+ ngx.say("plugins2.p2 shared: ", tab_copied.plugins2.p2 ==
tab.plugins2.p2)
+ }
+ }
+--- request
+GET /t
+--- response_body
+plugins.p1 shared: true
+plugins2.p2 shared: false
+--- no_error_log
+[error]