♻️ Refactor: Improve Req/Res Benchmarks#3693
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughRemoved two private helper methods from DefaultCtx and deleted their corresponding Ctx interface declarations. Refactored DefaultReq and DefaultRes internals to access fasthttp and app state directly via the shared DefaultCtx (r.c) while keeping public signatures unchanged. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant Req as DefaultReq
participant Res as DefaultRes
participant Ctx as DefaultCtx (r.c)
participant FH as fasthttp.RequestCtx
participant App as App (r.c.app)
rect rgba(230,240,255,0.5)
note over Req,Ctx: Request handling now uses r.c fields directly
Client->>Req: send request
Req->>Ctx: read r.c.fasthttp, r.c.app, r.c.methodInt, r.c.values
Ctx-->>Req: fasthttp request & app config
Req->>FH: direct header/query/body ops
end
rect rgba(230,255,230,0.5)
note over Res,Ctx: Response writing now uses r.c fields directly
Req->>Res: produce response
Res->>Ctx: access r.c.fasthttp.Response and r.c.app
Ctx-->>Res: response writer & app config
Res->>FH: set status/headers/body/write output
end
note over Ctx: Removed helpers: setMethodInt(), keepOriginalPath()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Summary of Changes
Hello @grivera64, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request refactors the internal implementation of Req (Request) and Res (Response) methods to significantly improve performance by reducing indirection. Previously, these methods often accessed underlying data through multiple function calls to DefaultCtx or other intermediate objects. The changes directly access the fasthttp.RequestCtx and App instance members, eliminating unnecessary function call overhead and leading to better benchmark results for request and response handling.
Highlights
- Performance Improvement: Reduced function call overhead in
ReqandResmethods by directly accessing underlyingfasthttp.RequestCtxandAppinstance members, rather than through intermediate helper methods. - Code Simplification: Eliminated redundant internal methods like
setMethodIntandkeepOriginalPathfromDefaultCtxand its interface, as direct access to fields makes them obsolete. - Direct Data Access: Updated numerous
ReqandResmethods to directly referencer.c.fasthttp(forfasthttp.RequestCtx),r.c.app(for theAppinstance), and otherDefaultCtxfields, streamlining data retrieval.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Pull Request Overview
This PR refactors the Req and Res method implementations to reduce function call overhead and improve performance by accessing data directly from the DefaultCtx struct instead of going through method indirection. The changes eliminate unnecessary function calls by replacing method calls like r.c.Method() with direct member access like r.c.member.
Key changes include:
- Direct access to fasthttp request/response objects instead of method calls
- Elimination of wrapper methods that add overhead
- Direct access to app instance and configuration
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| res.go | Refactored all response methods to access fasthttp.Response and app directly instead of through wrapper methods |
| req.go | Refactored all request methods to access fasthttp.Request and app directly instead of through wrapper methods |
| ctx_interface_gen.go | Removed interface methods that are no longer needed due to direct access pattern |
| ctx.go | Removed implementation of wrapper methods that are no longer used |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3693 +/- ##
==========================================
- Coverage 91.97% 91.81% -0.16%
==========================================
Files 114 114
Lines 11504 11500 -4
==========================================
- Hits 10581 10559 -22
- Misses 663 681 +18
Partials 260 260
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review
This pull request is a great performance-focused refactoring. The changes consistently remove method call indirections by accessing fields or methods on DefaultCtx directly from DefaultReq and DefaultRes. This is done for various methods related to request and response handling, such as accessing headers, body, app configuration, and request context. The removal of setMethodInt and keepOriginalPath from DefaultCtx and inlining their logic at call sites is also a clean way to achieve this. The changes are correct, well-aligned with the goal of improving benchmark performance, and should have a positive impact. I have reviewed the changes and found no issues.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
res.go (1)
478-478: Potential inconsistency in CBOR methodIn the CBOR method, line 478 uses
response.SetBodyRaw(raw)while other encoder methods like JSON (line 438) and MsgPack (line 458) also useresponse.SetBodyRaw(raw). However, I notice there's an inconsistent indentation or alignment that might have been introduced during refactoring.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
ctx.go(0 hunks)ctx_interface_gen.go(0 hunks)req.go(29 hunks)res.go(33 hunks)
💤 Files with no reviewable changes (2)
- ctx_interface_gen.go
- ctx.go
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: gaby
PR: gofiber/fiber#3193
File: middleware/adaptor/adaptor.go:111-111
Timestamp: 2024-11-10T23:44:13.704Z
Learning: In the `middleware/adaptor/adaptor.go` file of the Fiber framework, when updating context handling, replacing `c.Context()` with `c.RequestCtx()` is appropriate to access the `fasthttp.RequestCtx`.
Learnt from: gaby
PR: gofiber/fiber#3193
File: middleware/cache/cache_test.go:897-897
Timestamp: 2024-11-08T04:10:42.990Z
Learning: In the Fiber framework, `Context()` is being renamed to `RequestCtx()`, and `UserContext()` to `Context()` to improve clarity and align with Go's context conventions.
📚 Learning: 2024-11-10T23:44:13.704Z
Learnt from: gaby
PR: gofiber/fiber#3193
File: middleware/adaptor/adaptor.go:111-111
Timestamp: 2024-11-10T23:44:13.704Z
Learning: In the `middleware/adaptor/adaptor.go` file of the Fiber framework, when updating context handling, replacing `c.Context()` with `c.RequestCtx()` is appropriate to access the `fasthttp.RequestCtx`.
Applied to files:
res.goreq.go
📚 Learning: 2024-09-25T15:57:10.221Z
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/csrf/csrf_test.go:188-193
Timestamp: 2024-09-25T15:57:10.221Z
Learning: In the Fiber framework tests, using `ctx.Response.Header.Cookie` may not be suitable for parsing cookies from the response header, as it requires a `*Cookie` and fills it rather than returning a string value; thus, manual parsing of the `Set-Cookie` header may be necessary.
Applied to files:
res.go
📚 Learning: 2024-10-16T14:00:50.724Z
Learnt from: gaby
PR: gofiber/fiber#3170
File: ctx.go:1825-1826
Timestamp: 2024-10-16T14:00:50.724Z
Learning: In the Fiber framework, the `IsProxyTrusted()` function returns `true` when `TrustProxy` is `false`, maintaining compatibility with version 2 behavior.
Applied to files:
req.go
🧬 Code Graph Analysis (2)
res.go (2)
req.go (1)
DefaultReq(29-31)ctx_interface_gen.go (1)
Ctx(17-403)
req.go (2)
constants.go (9)
HeaderAccept(190-190)HeaderAcceptCharset(191-191)HeaderAcceptEncoding(192-192)HeaderAcceptLanguage(193-193)StatusUnsupportedMediaType(88-88)ErrNotImplemented(149-149)StatusNotImplemented(104-104)StatusRequestedRangeNotSatisfiable(89-89)HeaderContentRange(234-234)res.go (2)
DefaultRes(122-124)Cookie(101-113)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: unit (1.25.x, macos-latest)
- GitHub Check: unit (1.25.x, macos-13)
- GitHub Check: unit (1.25.x, windows-latest)
- GitHub Check: repeated
- GitHub Check: Compare
🔇 Additional comments (15)
req.go (7)
35-35: Direct fasthttp access improves performanceThese refactors eliminate the intermediate method calls and directly access
r.c.fasthttp.Request.Headerfor header operations, which aligns with the PR objective of reducing function-call overhead in benchmarks.Also applies to: 41-41, 47-47, 54-54, 61-61
92-92: Good optimization for decompression operationsCreating a single request reference and reusing it throughout the decompression logic reduces repeated field access overhead.
174-176: Direct field access for error responsesThe change from method calls to direct
r.c.DefaultResfield access is consistent with the refactoring pattern and reduces indirection.
252-253: Efficient local variable usage in Fresh()Good optimization by storing
r.c.applocally to avoid repeated field access in the hot path.
543-546: Method signature handling looks correctThe refactored Method() function properly uses direct field access for
r.c.methodIntandr.c.app.method()while maintaining the same public API behavior.Also applies to: 554-554
580-583: Optimized Params() with local variablesGood performance improvement by caching
r.c.app,r.c.Route(), and&r.c.valuesin local variables to reduce repeated field access in the loop.Also applies to: 594-594
808-809: Direct DefaultRes field access in Range()The change to
r.c.DefaultRes.Status()andr.c.DefaultRes.Set()is consistent with the overall refactoring pattern to reduce indirection.res.go (8)
128-128: Consistent direct field access patternThe changes to return
r.c.appandr.c.fasthttpdirectly eliminate wrapper method overhead, aligning with the PR's performance goals.Also applies to: 190-193
137-137: Header operations optimizedDirect access to
r.c.fasthttp.Response.Header.Peek()removes unnecessary indirection for header retrieval operations.Also applies to: 408-408
274-274: Response handling improvementsDirect fasthttp Response access for SetCookie and Response() methods reduces function call overhead as intended.
Also applies to: 308-308
324-325: Format() method refactoring aligns with req.go changesThe use of
r.c.DefaultReqfor Accept header processing and direct fasthttp Response access for Content-Type setting maintains consistency across the refactored codebase.Also applies to: 342-342, 353-353
368-368: AutoFormat() properly uses DefaultReqThe change to use
r.c.DefaultReq.Accepts()is consistent with the req.go refactoring and maintains proper separation of concerns.Also applies to: 378-378
432-432: Encoder methods optimizedAll encoder methods (JSON, MsgPack, CBOR, XML, JSONP) now use direct
r.c.app.configaccess for encoders, reducing indirection.Also applies to: 452-452, 472-472, 491-491, 514-514
758-758: SendFile optimization with path handlingThe changes properly maintain the original path for mutable params and original URL restoration, which is important for correct file serving behavior.
Also applies to: 788-788
965-965: End() method properly uses local context variableGood optimization by storing
r.c.fasthttpin a local variable to avoid repeated field access when writing response and closing connection.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
req.go (1)
580-596: Critical: compile-time error in Params due to pointer-to-slice usage.
values := &r.c.valuesmakesvaluesa pointer; subsequentlen(values)andvalues[i]are invalid and won’t compile. Use the slice directly.Apply this diff:
- app := r.c.app - route := r.c.Route() - values := &r.c.values + app := r.c.app + route := r.c.Route() + values := r.c.valuesNo other changes needed:
len(values)andvalues[i]will then compile and behave as intended.
🧹 Nitpick comments (6)
req.go (6)
92-92: Nit: simplify condition in multi-encoding decode path.The
decodesRealized > 0check is redundant (it’s always true inside the loop after increment). Slightly simplifies read and avoids a branch.Apply this diff:
- if i > 0 && decodesRealized > 0 { + if i > 0 { if i == len(encodings)-1 { tempBody := request.Body() *originalBody = make([]byte, len(tempBody)) copy(*originalBody, tempBody) } request.SetBodyRaw(body) }Also applies to: 118-126
304-309: Avoid allocating an interim map from Header.All(): use VisitAll instead.
Header.All()typically builds an intermediate structure.VisitAlliterates without that overhead.Apply this diff:
- app := r.c.app - headers := make(map[string][]string) - for k, v := range r.c.fasthttp.Request.Header.All() { - key := app.getString(k) - headers[key] = append(headers[key], app.getString(v)) - } + app := r.c.app + headers := make(map[string][]string) + r.c.fasthttp.Request.Header.VisitAll(func(k, v []byte) { + key := app.getString(k) + headers[key] = append(headers[key], app.getString(v)) + })
346-351: Avoid panic on non-TCP transports when extracting port.
RemoteAddr()may not always be*net.TCPAddr(e.g., Unix sockets). Prefer parsing the string form safely.Apply this diff:
- tcpaddr, ok := r.c.fasthttp.RemoteAddr().(*net.TCPAddr) - if !ok { - panic(errors.New("failed to type-assert to *net.TCPAddr")) - } - return strconv.Itoa(tcpaddr.Port) + hostPort := r.c.fasthttp.RemoteAddr().String() + _, port, err := net.SplitHostPort(hostPort) + if err != nil { + return "" + } + return port
368-368: Consolidate duplicate IP header scanning logic.
extractIPsFromHeaderandextractIPFromHeaderimplement nearly the same scanning/validation. Factor the scanner into a single helper that can return either the first valid IP or a slice to reduce maintenance and ensure consistent validation/rules.I can draft a shared zero-allocation scanner helper used by both functions if you want to include it in this PR.
Also applies to: 411-416, 429-429, 463-470
541-555: Grammar nit in comment; logic is correct.The comment under the invalid override branch has a small grammatical issue.
Apply this diff:
- // Provided override does not valid HTTP method, no override, return current method + // Provided override is not a valid HTTP method; do not override; return current method
628-663: Prefer direct header peeks over scanning all headers in Scheme().Iterating all headers is unnecessary overhead; check only the known proxy headers in order, trimming at the first comma. Behavior remains unchanged.
Apply this diff:
func (r *DefaultReq) Scheme() string { - ctx := r.c.fasthttp - if ctx.IsTLS() { - return schemeHTTPS - } - if !r.IsProxyTrusted() { - return schemeHTTP - } - - app := r.c.app - scheme := schemeHTTP - const lenXHeaderName = 12 - for key, val := range ctx.Request.Header.All() { - if len(key) < lenXHeaderName { - continue // Neither "X-Forwarded-" nor "X-Url-Scheme" - } - switch { - case bytes.HasPrefix(key, []byte("X-Forwarded-")): - if bytes.Equal(key, []byte(HeaderXForwardedProto)) || - bytes.Equal(key, []byte(HeaderXForwardedProtocol)) { - v := app.getString(val) - commaPos := strings.Index(v, ",") - if commaPos != -1 { - scheme = v[:commaPos] - } else { - scheme = v - } - } else if bytes.Equal(key, []byte(HeaderXForwardedSsl)) && bytes.Equal(val, []byte("on")) { - scheme = schemeHTTPS - } - - case bytes.Equal(key, []byte(HeaderXUrlScheme)): - scheme = app.getString(val) - } - } - return scheme + ctx := r.c.fasthttp + if ctx.IsTLS() { + return schemeHTTPS + } + if !r.IsProxyTrusted() { + return schemeHTTP + } + + app := r.c.app + h := &ctx.Request.Header + + if v := app.getString(h.Peek(HeaderXForwardedProto)); v != "" { + if i := strings.IndexByte(v, ','); i != -1 { + return v[:i] + } + return v + } + if v := app.getString(h.Peek(HeaderXForwardedProtocol)); v != "" { + if i := strings.IndexByte(v, ','); i != -1 { + return v[:i] + } + return v + } + if bytes.Equal(h.Peek(HeaderXForwardedSsl), []byte("on")) { + return schemeHTTPS + } + if v := app.getString(h.Peek(HeaderXUrlScheme)); v != "" { + return v + } + return schemeHTTP }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
req.go(28 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: gaby
PR: gofiber/fiber#3193
File: middleware/adaptor/adaptor.go:111-111
Timestamp: 2024-11-10T23:44:13.704Z
Learning: In the `middleware/adaptor/adaptor.go` file of the Fiber framework, when updating context handling, replacing `c.Context()` with `c.RequestCtx()` is appropriate to access the `fasthttp.RequestCtx`.
Learnt from: gaby
PR: gofiber/fiber#3193
File: middleware/cache/cache_test.go:897-897
Timestamp: 2024-11-08T04:10:42.990Z
Learning: In the Fiber framework, `Context()` is being renamed to `RequestCtx()`, and `UserContext()` to `Context()` to improve clarity and align with Go's context conventions.
📚 Learning: 2024-11-10T23:44:13.704Z
Learnt from: gaby
PR: gofiber/fiber#3193
File: middleware/adaptor/adaptor.go:111-111
Timestamp: 2024-11-10T23:44:13.704Z
Learning: In the `middleware/adaptor/adaptor.go` file of the Fiber framework, when updating context handling, replacing `c.Context()` with `c.RequestCtx()` is appropriate to access the `fasthttp.RequestCtx`.
Applied to files:
req.go
📚 Learning: 2024-10-16T14:00:50.724Z
Learnt from: gaby
PR: gofiber/fiber#3170
File: ctx.go:1825-1826
Timestamp: 2024-10-16T14:00:50.724Z
Learning: In the Fiber framework, the `IsProxyTrusted()` function returns `true` when `TrustProxy` is `false`, maintaining compatibility with version 2 behavior.
Applied to files:
req.go
🧬 Code Graph Analysis (1)
req.go (3)
constants.go (9)
HeaderAccept(190-190)HeaderAcceptCharset(191-191)HeaderAcceptEncoding(192-192)HeaderAcceptLanguage(193-193)StatusUnsupportedMediaType(88-88)ErrNotImplemented(149-149)StatusNotImplemented(104-104)StatusRequestedRangeNotSatisfiable(89-89)HeaderContentRange(234-234)res.go (1)
DefaultRes(122-124)router.go (1)
Route(42-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Compare
- GitHub Check: repeated
🔇 Additional comments (8)
req.go (8)
35-35: Good switch to direct header access via fasthttp.PeekAll (less indirection).This reduces function-call overhead and avoids intermediate allocations. Looks correct across Accepts*, AcceptsCharsets, Encodings, and Languages.
Also applies to: 41-41, 47-47, 54-54, 61-61
144-144: Body decoding refactor is sound and preserves original body.
- Using a local
request := &r.c.fasthttp.Requestavoids repeated indirection.- On error, signaling with
DefaultRes.SendStatusand returning a small error body is acceptable, and the original body is restored when needed.- Immutable path handled correctly.
Also applies to: 174-177, 181-183
190-190: RequestCtx() now returns the fasthttp context directly — aligned with v3 direction.Matches ongoing renames from prior work (Context → RequestCtx). Good.
199-199: Direct Cookie/FormValue reads with app.getString are OK.Avoids extra allocations while keeping semantics. LGTM.
Also applies to: 221-221
231-231: Fresh() optimization looks good.Using local references to request header and response avoids wrapper hops; ETag/Last-Modified logic preserved.
Also applies to: 252-254
807-809: Range: correct 416 handling with Content-Range header.Using DefaultRes to set status and Content-Range is correct per RFC semantics.
882-887: IsProxyTrusted logic matches v2 compatibility.Returning true when TrustProxy is false and validating loopback/private/link-local first aligns with prior behavior. Good.
919-924: getBody: immutable handling and direct fasthttp body access — LGTM.Copy on immutable path; otherwise returns the live body buffer. Correct and minimal.
Description
This PR is in continuation of issue #3632, to help improve benchmarks for Req and Res. This overhead is largely from the amount of indirection that occurs when accessing data stored inside of
DefaultCtxfrom inside of aDefaultReqorDefaultRes. This also occurs when some methods require bothReqandResmethods.The PR reduces this level of indirection by accessing data directly from
DefaultCtxorDefaultReq/DefaultRes.e.g.
r.c.Method()->r.c.DefaultReq.Method()r.Method()->r.c.memberFixes #3632
Changes introduced
ReqorResinterfaces.Type of change
Checklist
Before you submit your pull request, please make sure you meet these requirements:
/docs/directory for Fiber's documentation.