"GET /{resource}/latest" (registered at ...) conflicts with pattern "GET /posts/{id}" (registered at ...): GET /{resource}/latest and GET /posts/{id} both match some paths, like "/posts/latest". But neither is more specific than the other. GET /{resource}/latest matches "/resource/latest", but GET /posts/{id} doesn't. GET /posts/{id} matches "/posts/id", but GET /{resource}/latest doesn't. $ go run main.go
"GET /{resource}/latest" (registered at ...) conflicts with pattern "GET /posts/{id}" (registered at ...): GET /{resource}/latest and GET /posts/{id} both match some paths, like "/posts/latest". But neither is more specific than the other. GET /{resource}/latest matches "/resource/latest", but GET /posts/{id} doesn't. GET /posts/{id} matches "/posts/id", but GET /{resource}/latest doesn't. $ go run main.go では,どういうパターンの組み合わせが「競合」となるのか? >>>
an issue. With the precedence rules described here, checking a new pattern for conflicts seems to require looking at all existing patterns in the worst case. (Algorithm lovers, you are hereby nerd- sniped.) That means registering n patterns takes O(n2) time in the worst case. cf. https://github.com/golang/go/discussions/60227#discussioncomment-6204048 単純に考えるとパターン登録のたびに,登録済みの全パターンと競合をチェックする必要がある = 最悪の場合,計算量は となる Discovery Documentをもとに収集した約5000パターンのGoogle APIに対して,単純に競合チェックを実施 すると約1秒程度かかった(らしい) O(N ) 2
an issue. With the precedence rules described here, checking a new pattern for conflicts seems to require looking at all existing patterns in the worst case. (Algorithm lovers, you are hereby nerd- sniped.) That means registering n patterns takes O(n2) time in the worst case. cf. https://github.com/golang/go/discussions/60227#discussioncomment-6204048 単純に考えるとパターン登録のたびに,登録済みの全パターンと競合をチェックする必要がある = 最悪の場合,計算量は となる Discovery Documentをもとに収集した約5000パターンのGoogle APIに対して,単純に競合チェックを実施 すると約1秒程度かかった(らしい) ServeMuxでは,パターンの登録時にインデックスを作成しておき,インデックスから効率よく競合し得るパタ ーンを取得し,チェックすることで性能向上を図っている O(N ) 2
行う cf. https://github.com/golang/go/blob/go1.22.1/src/net/http/routing_index.go#L57 // Our simple indexing scheme doesn't try to prune multi // any of them can match the argument. if err := apply(idx.multis); err != nil { return err } if pat.lastSegment().s == "/" { // All paths that a dollar pattern matches end in a sl // an ordinary pattern matches do. So only other dolla // patterns can conflict with a dollar pattern. Furthe // dollar patterns must have the {$} in the same posit return apply(idx.segments[routingIndexKey{s: "/", pos: }
1番目にドル( / )を持つパターン(P4)と競合チェックを行う cf. https://github.com/golang/go/blob/go1.22.1/src/net/http/routing_index.go#L57 if pat.lastSegment().s == "/" { // All paths that a dollar pattern matches end in a sl // an ordinary pattern matches do. So only other dolla // patterns can conflict with a dollar pattern. Furthe // dollar patterns must have the {$} in the same posit return apply(idx.segments[routingIndexKey{s: "/", pos: } // For ordinary and multi patterns, the only conflicts c // or a pattern that has the same literal or a wildcard // position. // We could intersect all the possible matches at each p / d hi i f d h i i i h h f
-> 2個のパターン(P1, P2)と競合チェックを行う 0番目が posts or "" のパターン -> 3個(P1, P2, P4) 1番目が latest or "" のパターン -> 2個(P1, P2) cf. https://github.com/golang/go/blob/go1.22.1/src/net/http/routing_index.go#L57 // For ordinary and multi patterns, the only conflicts c // or a pattern that has the same literal or a wildcard // position. // We could intersect all the possible matches at each p // do something simpler: we find the position with the f var lmin, wmin []*pattern min := math.MaxInt hasLit := false for i, seg := range pat.segments { if seg.multi { break }
-> segments に登録されている全パターンと競合チェックを行う cf. https://github.com/golang/go/blob/go1.22.1/src/net/http/routing_index.go#L57 // This pattern is all wildcards. // Check it against everything. for _, pats := range idx.segments { apply(pats) } return err