Skip to content

fix(plugin): preserve plugin-conf state across consumer route merge - #13757

Open
AlinsRan wants to merge 1 commit into
apache:masterfrom
AlinsRan:perf/deepcopy-shallow-prefix
Open

fix(plugin): preserve plugin-conf state across consumer route merge#13757
AlinsRan wants to merge 1 commit into
apache:masterfrom
AlinsRan:perf/deepcopy-shallow-prefix

Conversation

@AlinsRan

@AlinsRan AlinsRan commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Description

Several plugins cache request-time state directly on their conf object:

  • ai-proxy-multi: DNS resolution results — instance_conf._dns_nodes, _nodes_ver, _resolved_endpoint, _healthy_dns_nodes
  • upstream.lua: up_conf._nodes_ver
  • elasticsearch-logger: the probed conf._version

merge_consumer_route deep-copies the whole route_conf, including value.plugins:

local new_route_conf = core.table.deepcopy(route_conf)

So every consumer request that misses the merged_route lru cache produces a fresh plugin conf object and loses that cached state — DNS is re-resolved, the ES version is re-probed, and with multiple consumers the state is fragmented per consumer instead of shared. Without consumer auth the route conf is never copied, so this only regresses on the consumer-auth path.

This adds an opts.shallow_prefix option to core.table.deepcopy that shallow-copies members whose parent path starts with the given prefix, and uses { shallow_prefix = "self.value.plugins" } in merge_consumer_route so the plugin conf objects keep their identity.

The plugins container itself is still a fresh table, so overwriting its keys during the merge does not mutate the original; only the plugin confs that carry state stay shared by reference — which restores the non-consumer behavior (a single source of truth for that route's plugin state).

Which issue(s) this PR fixes

Plugin state cached on conf (DNS resolution, probed versions) is lost/re-computed on the consumer-auth path because of the plugins deep-copy.

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change (t/core/table.t TEST 13 for the option)
  • I have updated the documentation accordingly

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. performance generate flamegraph for the current PR labels Jul 29, 2026
@AlinsRan AlinsRan closed this Jul 29, 2026
@AlinsRan AlinsRan reopened this Jul 29, 2026
Several plugins cache request-time state on their conf object: ai-proxy-multi
keeps DNS resolution results (_dns_nodes, _nodes_ver, _resolved_endpoint),
upstream.lua keeps _nodes_ver, elasticsearch-logger keeps the probed _version.

merge_consumer_route deep-copies the whole route_conf, including value.plugins,
so every consumer request that misses the merged_route lru cache produces a
fresh plugin conf object and loses that cached state -- DNS is re-resolved, the
ES version is re-probed, and with multiple consumers the state is fragmented
per consumer instead of shared. Without consumer auth the route conf is never
copied, so this only regresses on the consumer path.

Adds an opts.shallow_prefix option to core.table.deepcopy that shallow-copies
members whose parent path starts with the given prefix, and uses
{ shallow_prefix = 'self.value.plugins' } in merge_consumer_route so the plugin
conf objects keep their identity. The plugins container itself is still a fresh
table, so overwriting its keys in the merge does not mutate the original; only
the plugin confs that carry state stay shared by reference, which restores the
non-consumer behavior.

Adds t/core/table.t TEST 13 for opts.shallow_prefix.

Signed-off-by: AlinsRan <alinsran@apache.org>
@AlinsRan
AlinsRan force-pushed the perf/deepcopy-shallow-prefix branch from 49750c5 to a16d026 Compare July 29, 2026 14:03
@AlinsRan AlinsRan changed the title perf(plugin): avoid deep-copying the plugins subtree in merge_consumer_route fix(plugin): preserve plugin-conf state across consumer route merge Jul 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts how route configuration is copied during merge_consumer_route so plugin configuration tables can retain request-time cached state (e.g., resolved DNS nodes or probed backend versions) across consumer-route merges, matching the non-consumer behavior where plugin conf identity is preserved.

Changes:

  • Added a shallow_prefix option to core.table.deepcopy to shallow-copy table members under a path prefix.
  • Updated merge_consumer_route to shallow-copy self.value.plugins so plugin conf objects keep identity while still copying the plugins container table.
  • Added a regression test covering shallow_prefix behavior in t/core/table.t.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
apisix/core/table.lua Extends deepcopy with a prefix-based shallow-copy option to preserve selected subtree identities.
apisix/plugin.lua Uses shallow_prefix = "self.value.plugins" when copying route conf during consumer merge to preserve plugin conf state.
t/core/table.t Adds a test validating that shallow-prefix copying preserves nested table identity while allowing safe container overwrites.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apisix/core/table.lua
Comment on lines 133 to 136
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 str_has_prefix(parent, opts.shallow_prefix))) then
copy[orig_key] = orig_value
Comment thread apisix/plugin.lua
Comment on lines +821 to +824
-- the plugins subtree is fully overwritten below, so there is no need to
-- deep-copy it; shallow-copy that subtree to avoid the wasted work
local new_route_conf = core.table.deepcopy(route_conf,
{ shallow_prefix = "self.value.plugins" })

@membphis membphis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance generate flamegraph for the current PR size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants