Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/resty/radixtree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ local mt = { __index = _M, __gc = gc_free }

local function sort_route(route_a, route_b)
if route_a.priority == route_b.priority then
if #route_a.path_org == #route_b.path_org then
return (route_a.vars_len or 0) > (route_b.vars_len or 0)
end
return #route_a.path_org > #route_b.path_org
end
Comment on lines 206 to 212
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

The new tiebreaker is triggered when #route_a.path_org == #route_b.path_org, which is broader than the PR’s stated intent (“same priority and uri”). Different paths with the same length (possible in the same routes bucket for param/prefix matches) would now be reordered by vars_len, changing routing precedence beyond identical URIs. If the intent is only to break ties for identical paths, consider checking route_a.path_org == route_b.path_org (string equality) before comparing vars_len, and otherwise keep the existing ordering rules.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

fixed

return (route_a.priority or 0) > (route_b.priority or 0)
Expand Down Expand Up @@ -438,6 +441,7 @@ local function common_route_data(path, route, route_opts, global_opts)
error("failed to handle expression: " .. err, 2)
end
route_opts.vars = route_expr
route_opts.vars_len = #route.vars
end
Comment on lines 434 to 445
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

vars_len is set as #route.vars, but the vars DSL (lua-resty-expr) can include non-condition tokens (e.g., boolean operators like "and"/"or"), so #route.vars may not reflect the number of actual conditions. This can lead to incorrect “more-specific” ordering. Consider computing vars_len as the count of condition nodes (e.g., elements whose type is table) rather than the raw array length, and add a test case that includes boolean operators to lock in the intended behavior.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Valid observation, but over-engineering. The concern is real: a route with vars = {{"AND", cond1, cond2}} gets vars_len = 1 while vars = {cond1, cond2} gets vars_len = 2, even though they express the same thing.
However:

  • In practice, the flat form is what all real users (including APISIX) use. The nested {"AND", ...} grouping syntax is rarely used.
  • Counting actual condition nodes would require recursive traversal and adds meaningful complexity for an edge case.
  • Even with the imprecision, the behavior is still correct — it doesn't break routing, it just has a slight inaccuracy in sort order for an unusual DSL form.
    ps: might need some input from maintainers


local hosts = route.hosts
Expand Down
35 changes: 35 additions & 0 deletions t/vars.t
Original file line number Diff line number Diff line change
Expand Up @@ -566,3 +566,38 @@ metadata /aa
metadata /aa
nil
nil



=== TEST 20: match max vars condition
--- config
location /t {
content_by_lua_block {
local radix = require("resty.radixtree")
local rx = radix.new({
{
paths = {"/aa"},
metadata = "metadata /aa",
vars = {
{"arg_k", "==", "v"},
},
},
{
paths = {"/aa"},
metadata = "metadata /aa2",
vars = {
{"arg_k", "==", "v"},
{"arg_k2", "==", "v2"},
},
},
})

ngx.say(rx:match("/aa", {vars = ngx.var}))
}
}
--- request
GET /t?k=v&k2=v2
--- no_error_log
[error]
--- response_body
metadata /aa2
Loading