Skip to content

Commit 9be4804

Browse files
authored
Merge pull request #4044 from gofiber/copilot/sub-pr-4042
🐛 fix: eliminate data races in parallel router benchmarks
2 parents 9524cbd + 16e596c commit 9be4804

File tree

1 file changed

+39
-18
lines changed

1 file changed

+39
-18
lines changed

router_test.go

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2016,14 +2016,15 @@ func Test_AddRoute_MergeHandlers(t *testing.T) {
20162016
}
20172017

20182018
func Benchmark_App_RebuildTree_Parallel(b *testing.B) {
2019-
app := New()
2020-
registerDummyRoutes(app)
20212019
b.ReportAllocs()
20222020
b.ResetTimer()
20232021
b.RunParallel(func(pb *testing.PB) {
2022+
// Each worker gets its own App instance to avoid data races on shared state
2023+
localApp := New()
2024+
registerDummyRoutes(localApp)
20242025
for pb.Next() {
2025-
app.routesRefreshed = true
2026-
app.RebuildTree()
2026+
localApp.routesRefreshed = true
2027+
localApp.RebuildTree()
20272028
}
20282029
})
20292030
}
@@ -2036,17 +2037,24 @@ func Benchmark_App_MethodNotAllowed_Parallel(b *testing.B) {
20362037
app.All("/this/is/a/", h)
20372038
app.Get("/this/is/a/dummy/route/oke", h)
20382039
appHandler := app.Handler()
2039-
c := &fasthttp.RequestCtx{}
2040-
c.Request.Header.SetMethod("DELETE")
2041-
c.URI().SetPath("/this/is/a/dummy/route/oke")
20422040
b.RunParallel(func(pb *testing.PB) {
2041+
// Each worker gets its own RequestCtx to avoid data races
2042+
c := &fasthttp.RequestCtx{}
2043+
c.Request.Header.SetMethod("DELETE")
2044+
c.URI().SetPath("/this/is/a/dummy/route/oke")
20432045
for pb.Next() {
20442046
appHandler(c)
20452047
}
20462048
})
2047-
require.Equal(b, 405, c.Response.StatusCode())
2048-
require.Equal(b, MethodGet+", "+MethodHead, string(c.Response.Header.Peek("Allow")))
2049-
require.Equal(b, utils.StatusMessage(StatusMethodNotAllowed), string(c.Response.Body()))
2049+
2050+
// Single-threaded verification on a fresh context to preserve correctness checks
2051+
verifyCtx := &fasthttp.RequestCtx{}
2052+
verifyCtx.Request.Header.SetMethod("DELETE")
2053+
verifyCtx.URI().SetPath("/this/is/a/dummy/route/oke")
2054+
appHandler(verifyCtx)
2055+
require.Equal(b, 405, verifyCtx.Response.StatusCode())
2056+
require.Equal(b, MethodGet+", "+MethodHead, string(verifyCtx.Response.Header.Peek("Allow")))
2057+
require.Equal(b, utils.StatusMessage(StatusMethodNotAllowed), string(verifyCtx.Response.Body()))
20502058
}
20512059

20522060
func Benchmark_Router_NotFound_Parallel(b *testing.B) {
@@ -2158,6 +2166,7 @@ func Benchmark_Router_Next_Parallel(b *testing.B) {
21582166
c := acquireDefaultCtxForRouterBenchmark(b, app, request)
21592167
for pb.Next() {
21602168
c.indexRoute = -1
2169+
//nolint:errcheck // Benchmark hot path - error checked in verification
21612170
_, _ = app.next(c)
21622171
}
21632172
})
@@ -2192,20 +2201,24 @@ func Benchmark_Router_Next_Default_Immutable_Parallel(b *testing.B) {
21922201
}
21932202

21942203
func Benchmark_Route_Match_Parallel(b *testing.B) {
2195-
var match bool
2196-
var params [maxParams]string
21972204
parsed := parseRoute("/user/keys/:id")
21982205
route := &Route{use: false, root: false, star: false, routeParser: parsed, Params: parsed.params, path: "/user/keys/:id", Path: "/user/keys/:id", Method: "DELETE"}
21992206
route.Handlers = append(route.Handlers, func(_ Ctx) error {
22002207
return nil
22012208
})
22022209
b.RunParallel(func(pb *testing.PB) {
2210+
// Each worker gets its own local variables to avoid data races
2211+
var params [maxParams]string
22032212
for pb.Next() {
2204-
match = route.match("/user/keys/1337", "/user/keys/1337", &params)
2213+
_ = route.match("/user/keys/1337", "/user/keys/1337", &params)
22052214
}
22062215
})
2216+
2217+
// Single-threaded verification to preserve correctness checks
2218+
var verifyParams [maxParams]string
2219+
match := route.match("/user/keys/1337", "/user/keys/1337", &verifyParams)
22072220
require.True(b, match)
2208-
require.Equal(b, []string{"1337"}, params[0:len(parsed.params)])
2221+
require.Equal(b, []string{"1337"}, verifyParams[0:len(parsed.params)])
22092222
}
22102223

22112224
func Benchmark_Route_Match_Star_Parallel(b *testing.B) {
@@ -2295,20 +2308,28 @@ func Benchmark_Router_GitHub_API_Parallel(b *testing.B) {
22952308
app := New()
22962309
registerDummyRoutes(app)
22972310
app.startupProcess()
2298-
c := &fasthttp.RequestCtx{}
2299-
var match bool
2300-
var err error
23012311
b.ResetTimer()
23022312
for i := range routesFixture.TestRoutes {
23032313
b.RunParallel(func(pb *testing.PB) {
2314+
// Each worker gets its own RequestCtx and local variables to avoid data races
2315+
c := &fasthttp.RequestCtx{}
23042316
c.Request.Header.SetMethod(routesFixture.TestRoutes[i].Method)
23052317
for pb.Next() {
23062318
c.URI().SetPath(routesFixture.TestRoutes[i].Path)
23072319
ctx := acquireDefaultCtxForRouterBenchmark(b, app, c)
2308-
match, err = app.next(ctx)
2320+
//nolint:errcheck // Benchmark hot path - error checked in verification
2321+
_, _ = app.next(ctx)
23092322
app.ReleaseCtx(ctx)
23102323
}
23112324
})
2325+
2326+
// Single-threaded verification on a fresh context to preserve correctness checks
2327+
verifyC := &fasthttp.RequestCtx{}
2328+
verifyC.Request.Header.SetMethod(routesFixture.TestRoutes[i].Method)
2329+
verifyC.URI().SetPath(routesFixture.TestRoutes[i].Path)
2330+
verifyCtx := acquireDefaultCtxForRouterBenchmark(b, app, verifyC)
2331+
match, err := app.next(verifyCtx)
2332+
app.ReleaseCtx(verifyCtx)
23122333
require.NoError(b, err)
23132334
require.True(b, match)
23142335
}

0 commit comments

Comments
 (0)