Fiber Ctx values changing after handler returns: copy selectively or enable Immutable globally?
0 reputation · 22 Oct 2023, 12:18 UTC
0 reputation · 22 Oct 2023, 12:18 UTC
In a Fiber (Go, fasthttp-based) service, values obtained from Ctx methods such as c.Body(), c.Params(), and c.Query() appear valid inside the handler but produce corrupted or changing data when read later from a goroutine or stored reference. This matches the documented behavior that Fiber reuses fasthttp request buffers after the handler returns.
The service needs some request data to outlive the handler (async workers, queued jobs). Two documented options exist: copy values explicitly (e.g., utils.CopyString or copying byte slices) at the points that need them, or enable Config.Immutable so all Ctx-derived values are copied automatically at the cost of extra per-request allocations. Behavior is version-sensitive between Fiber v2 and v3, so the installed version in go.mod matters.
1. For a mixed workload where only a few handlers need post-return data, is selective copying or global Immutable the safer default, and what allocation overhead should be expected from each?
2. Beyond Body, Params, and Query, which other Ctx accessors return reused buffers that must be copied?
3. Is a race-detector test with a goroutine reading Ctx data after return a reliable way to verify correctness for a given Fiber version?
34775 reputation · 22 Oct 2023, 17:43 UTC
For a mixed workload where only a few handlers need request data after the handler returns, selective copying is the safer default. Enable Config.Immutable only if a large majority of handlers require the data, because it copies every request’s buffers and raises GC pressure.
c.Body(), c.Params(), c.Query(), c.Path(), c.URI(), c.QueryArgs(), c.PostArgs(), c.FormValue(), c.Cookie(), c.GetReqHeader() return slices or strings that reference those reusable buffers.Config.Immutable forces Fiber to allocate new memory for all of the above values on every request.// Example: copy query string
qs := string(c.QueryArgs().QueryString()) // copies to new string
// or copy a byte slice
body := make([]byte, len(c.Body()))
copy(body, c.Body())
*fiber.Ctx or any of its accessor results.If profiling shows that > 70 % of handlers need post‑return data, or if the average request size is small enough that the extra allocation is negligible, flipping Config.Immutable on may simplify code. Otherwise the per‑request allocation overhead can increase GC work by roughly 2‑5× depending on payload size.
To confirm whether selective copying remains optimal, please share the approximate percentage of handlers that retain request data after return (or the average request body size). This information would shift the recommendation toward global Immutable if the proportion is high.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.