Skip to content

fix: replace module-level mutable tables with per-call allocation#13369

Merged
nic-6443 merged 3 commits into
apache:masterfrom
nic-6443:fix/module-level-mutable-tables
May 14, 2026
Merged

fix: replace module-level mutable tables with per-call allocation#13369
nic-6443 merged 3 commits into
apache:masterfrom
nic-6443:fix/module-level-mutable-tables

Conversation

@nic-6443

Copy link
Copy Markdown
Member

Several plugins and routers declared local tbl = {} at module scope and reused the same table across coroutines via core.table.clear(). When a function yields (e.g. ctx.var[...] lookup or shdict op), another concurrent request on the same worker can re-enter and mutate the shared table, causing cross-request state pollution.

Affected high-risk sites (yield in function body):

  • prometheus/exporter.lua: inner_tab_arr in gen_arr(), extra_labels_tbl in extra_labels() — replaced with per-call local table
  • proxy-cache/util.lua: tmp in generate_complex_value() — replaced with core.tablepool.fetch/release
  • redirect.lua: tmp in concat_new_uri() — replaced with core.tablepool.fetch/release

Affected low-risk sites (preventive, no current yield):

  • api_router.lua: match_opts in match() — replaced with core.tablepool.fetch/release
  • control/router.lua: match_opts in match() — replaced with core.tablepool.fetch/release
  • stream/router/ip_port.lua: match_opts in match() — replaced with core.tablepool.fetch/release

This follows the same fix pattern as the historical radixtree_host_uri route-mismatch fix (PR #10198).

Several plugins and routers declared `local tbl = {}` at module scope
and reused the same table across coroutines via `core.table.clear()`.
When a function yields (e.g. `ctx.var[...]` lookup or shdict op),
another concurrent request on the same worker can re-enter and mutate
the shared table, causing cross-request state pollution.

Affected high-risk sites (yield in function body):
- prometheus/exporter.lua: inner_tab_arr in gen_arr(), extra_labels_tbl
  in extra_labels() — use per-call local table
- proxy-cache/util.lua: tmp in generate_complex_value() — use tablepool
- redirect.lua: tmp in concat_new_uri() — use tablepool

Affected low-risk sites (preventive, no current yield):
- api_router.lua: match_opts in match() — use tablepool
- control/router.lua: match_opts in match() — use tablepool
- stream/router/ip_port.lua: match_opts in match() — use tablepool

This follows the same fix pattern as the historical radixtree_host_uri
route-mismatch fix (PR apache#10198).
Copilot AI review requested due to automatic review settings May 13, 2026 10:34
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. bug Something isn't working labels May 13, 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

Note

Copilot was unable to run its full agentic suite in this review.

This PR removes module-level mutable tables that were reused across concurrent requests (coroutines) and replaces them with per-call allocations or core.tablepool.fetch/release to prevent cross-request state pollution when functions yield.

Changes:

  • Replaced module-scope match_opts tables with core.tablepool.fetch/release in multiple routers.
  • Replaced module-scope temporary arrays in redirect/proxy-cache utilities with core.tablepool.fetch/release.
  • Replaced reuse of module-scope tables in Prometheus exporter helpers with per-call table allocations.

Reviewed changes

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

Show a summary per file
File Description
apisix/stream/router/ip_port.lua Uses tablepool for match_opts during TLS router dispatch.
apisix/plugins/redirect.lua Uses tablepool for URI segment concatenation temp table.
apisix/plugins/proxy-cache/util.lua Uses tablepool for complex value concatenation temp table.
apisix/plugins/prometheus/exporter.lua Switches helper tables to per-call allocations to avoid shared state.
apisix/control/router.lua Uses tablepool for match_opts during control-plane route dispatch.
apisix/api_router.lua Uses tablepool for match_opts during API router dispatch.
Comments suppressed due to low confidence (2)

apisix/plugins/redirect.lua:1

  • tmp is released only on the normal path. If an exception is raised anywhere after fetch (e.g., unexpected runtime error inside the loop/body), the pooled table will not be returned to the pool, which can degrade long-running worker behavior. Consider wrapping the body between fetch and release so core.tablepool.release(...) is guaranteed to run even when an error occurs (then rethrow/propagate the error).
--

apisix/plugins/proxy-cache/util.lua:1

  • Same pooling concern as in redirect.lua: if any error occurs after fetch (e.g., from resolve_var or other runtime failures), tmp will not be released back to the pool. Ensure core.tablepool.release(...) executes on both success and error paths to avoid leaking pooled tables.
--

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

Comment thread apisix/control/router.lua Outdated
Comment thread apisix/api_router.lua
f
Signed-off-by: Nic <[email protected]>
@nic-6443
nic-6443 merged commit ef1970a into apache:master May 14, 2026
19 checks passed
@nic-6443
nic-6443 deleted the fix/module-level-mutable-tables branch May 14, 2026 09:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants