mirror of
https://github.com/infinite-iroha/touka.git
synced 2026-06-15 16:47:38 +08:00
fix: streamline route matcher backtracking
Avoid rebuilding skipped-node state during wildcard fallback so the matcher no longer loops on the same static branch and stops allocating on the hot path. Add focused route benchmarks and regression coverage to keep the optimized path stable.
This commit is contained in:
parent
b1ce4d584e
commit
6acac9edce
5 changed files with 240 additions and 41 deletions
66
tree_test.go
66
tree_test.go
|
|
@ -11,6 +11,7 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Used as a workaround since we can't compare functions or their addresses
|
||||
|
|
@ -39,6 +40,23 @@ func getSkippedNodes() *[]skippedNode {
|
|||
return &ps
|
||||
}
|
||||
|
||||
func getValueWithTimeout(t *testing.T, tree *node, path string, unescape bool) nodeValue {
|
||||
t.Helper()
|
||||
|
||||
resultCh := make(chan nodeValue, 1)
|
||||
go func() {
|
||||
resultCh <- tree.getValue(path, getParams(), getSkippedNodes(), unescape)
|
||||
}()
|
||||
|
||||
select {
|
||||
case value := <-resultCh:
|
||||
return value
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatalf("lookup for path %q timed out, likely stuck in backtracking", path)
|
||||
return nodeValue{}
|
||||
}
|
||||
}
|
||||
|
||||
func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes ...bool) {
|
||||
unescape := false
|
||||
if len(unescapes) >= 1 {
|
||||
|
|
@ -1104,3 +1122,51 @@ func TestComplexBacktrackingWithCatchAll(t *testing.T) {
|
|||
t.Errorf("处理路径 '%s' 时参数不匹配: \n 得到: %v\n 想要: %v", reqPath, *value.params, wantParams)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktrackingFallsThroughToWildcardBranch(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
routes []string
|
||||
requestPath string
|
||||
wantFullPath string
|
||||
wantParams Params
|
||||
}{
|
||||
{
|
||||
name: "param route after static dead end",
|
||||
routes: []string{"/foo/bar", "/foo/:id/details"},
|
||||
requestPath: "/foo/bar/details",
|
||||
wantFullPath: "/foo/:id/details",
|
||||
wantParams: Params{{Key: "id", Value: "bar"}},
|
||||
},
|
||||
{
|
||||
name: "catch-all route after static dead end",
|
||||
routes: []string{"/foo/bar", "/foo/:id/*rest"},
|
||||
requestPath: "/foo/bar/baz.txt",
|
||||
wantFullPath: "/foo/:id/*rest",
|
||||
wantParams: Params{
|
||||
{Key: "id", Value: "bar"},
|
||||
{Key: "rest", Value: "/baz.txt"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tree := &node{}
|
||||
for _, route := range tt.routes {
|
||||
tree.addRoute(route, fakeHandler(route))
|
||||
}
|
||||
|
||||
value := getValueWithTimeout(t, tree, tt.requestPath, false)
|
||||
if value.handlers == nil {
|
||||
t.Fatalf("expected handlers for %q", tt.requestPath)
|
||||
}
|
||||
if value.fullPath != tt.wantFullPath {
|
||||
t.Fatalf("expected full path %q for %q, got %q", tt.wantFullPath, tt.requestPath, value.fullPath)
|
||||
}
|
||||
if value.params == nil || !reflect.DeepEqual(*value.params, tt.wantParams) {
|
||||
t.Fatalf("expected params %v for %q, got %v", tt.wantParams, tt.requestPath, value.params)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue