v4: stop implementing context.Context on Ctx
Problem
ctx.go:38 asserts that *DefaultCtx satisfies context.Context:
_ context.Context = (*DefaultCtx)(nil) // Compile-time checkDefaultCtx is recycled through a sync.Pool and mutated during the request. A context.Context is meant to be an immutable value that you derive from and hand across goroutine boundaries. Those two models do not fit, and the mismatch keeps producing bugs rather than one bug:
Deadline(),Done()andErr()can only ever be no-ops. A non-nilDone()makescontext.propagateCancelanddatabase/sqlspawn watcher goroutines that retain the pooled*DefaultCtxpastrelease(), and readingErr()back through the recycled object panics withcontext: internal error: missing cancel errorin a goroutine Fiber cannot recover. See #4560 for the full derivation.Value()reads the fasthttpuserValuesslice thatLocals()writes, so it is not safe for concurrent use, which the interface requires.- Users write
db.QueryContext(c, ...)because the compiler accepts it, and get no cancellation. It is silent, and it is the natural thing to write. #4335 is one report of this; the docs were teaching the same pattern until #4560.
Every fix so far has been a workaround for the assertion above.
Proposal for v4
Drop the assertion and stop implementing context.Context on Ctx. c.Context() already returns a real context.Context and is the documented way to get cancellation, deadlines, and something safe to use after the handler returns.
The payoff is that the compiler rejects c wherever a context.Context is expected, so the entire class of "it compiled but nothing was ever canceled" disappears at the call site instead of in production.
Migration
Mechanical and greppable: f(c) becomes f(c.Context()) wherever f takes a context.Context. Worth a note in the v4 migration guide with exactly that sentence.
Open questions
- Keep
Value()as a plain method forLocalsinterop, or drop it too? - Is there a use for a small adapter (
c.AsContext()), or isc.Context()enough?
Refs #4560, #4335
Source: gofiber/fiber