fix(traffic-label): cache the compiled match expressions outside the plugin config - #13901
Merged
Merged
Conversation
…plugin config access() compiled each rule's `match` expression once and stored the result in `conf.rules_arr`, i.e. inside the plugin configuration itself. For the `ipmatch` operator resty.expr compiles into an ipmatcher, whose `ipv4` and `ipv4_mask` lookup tables are keyed by integers; cjson rejects such a table as an "excessively sparse array". From the first request that reaches the plugin onwards the route configuration therefore could no longer be JSON encoded: with the error log at info level every line that dumps the route (`matched route`, `insert uri route`, `route conf`) came out empty, and each one emitted two "failed to encode" warnings. Cache the compiled expressions in an lrucache keyed by `conf.rules`, the way the round-robin objects are already cached, and leave the configuration untouched.
Keep the compiled expression on the rule itself, behind a metatable, rather than in a module-level lrucache. cjson does not walk metatables, so the expression stays invisible to serialisation while its lifetime matches the configuration exactly: compiled once, no TTL, no eviction cap and no recompilation, which is what the original `conf.rules_arr` cache provided. This is the same technique already used to keep runtime fields out of serialisation in body-transformer (apache#11770) and for the plugin `_meta.parent`.
`rules` items have no `additionalProperties: false`, so a rule may carry any property, `_expr` included. A truthy value skipped the compilation and a `false` one shadowed the metatable entry, and either way `rule._expr:eval()` then indexed a non-table and aborted the request with a 500. Key the cached expression on a module-local table instead: a table key cannot be expressed in JSON, so no configuration can collide with it. The same hazard existed before this branch, through the `conf.rules_arr` field.
JSON null is stored as-is and decodes to a userdata, which is truthy, so it skips the compilation exactly like `true` does and then aborts the request. Restructure the block as well: three rules of nested JSON overflowed the inlined-Lua size limit of `content_by_lua_block`.
This reverts the module-local key and its tests. A rule carrying a property named `_expr` does abort the request, but the same input shape already does so on master through `conf.rules_arr`, so this branch is no better or worse than the current behaviour there. Keeping it out of scope.
shreemaan-abhishek
approved these changes
Sep 1, 2026
AlinsRan
approved these changes
Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
traffic-label'saccess()compiles each rule'smatchexpression on the first request and stores the result inconf.rules_arr— inside the plugin configuration itself.For the
ipmatchoperator,resty.exprcompiles the right-hand side into anipmatcher(resty/expr/v1.lua), and an ipmatcher'sipv4/ipv4_masklookup tables are keyed by integers (the 32-bit address and the prefix length). cjson refuses to serialise such a table:So from the first request that reaches the plugin onwards, the route configuration can no longer be JSON encoded. With the error log at
infolevel, every line that dumps the route comes out empty and each one emits two warnings (log_wrapperevaluates a__tostringargument twice):matched route,insert uri routeandroute confare all affected, i.e. the debugging output you turnedinfoon for is exactly what is lost.Keep the compiled expression on the rule itself but behind a metatable, so it stays invisible to serialisation while its lifetime still matches the configuration exactly — compiled once, no TTL, no eviction cap, no recompilation. This is the same technique #11770 introduced for
body-transformerand thatset_plugins_meta_parentuses for the plugin_meta.parent.Which issue(s) this PR fixes
N/A
Checklist