fix(api): dynamic handler loses request cancellation and trace context#1019
Merged
Conversation
…equests The dynamic handler passed the *gin.Context itself where a context.Context is expected, while the blocking handler correctly passed c.Request.Context(). Gin only proxies Done/Err/Value to the request context when Engine.ContextWithFallback is enabled, which it is not: the gin context has a nil Done() channel and its Value() stops at gin keys. Two silent consequences on the dynamic path (the main user-facing endpoint): client disconnects and server shutdown never cancelled session-start work, and the otelgin span was invisible downstream, so every provider call under a dynamic request was recorded as an orphan root span while blocking-path traces looked fine. Regression tests pin the contract on both handlers: a value stored on the request context must be visible to the session layer, and a cancelled request context must be observed as cancelled.
Test Results✅ All tests passed! | 759 tests in 144.663s
|
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Finding
The dynamic handler passed the
*gin.Contextitself where acontext.Contextis expected:while the blocking handler correctly passed
c.Request.Context(). Gin only proxiesDone()/Err()/Value()through to the underlying request context whenEngine.ContextWithFallbackis enabled - and it is enabled nowhere in this codebase (verified: zero occurrences). Without it:c.Done()returns a nil channel andc.Err()returns nil - cancellation never propagates. A client that disconnects (or a server shutting down) does not cancel the session-start work started on its behalf.c.Value(...)stops at gin's own keys and never reaches the request context - so the span installed byotelgin.Middlewareis invisible downstream. Every provider call under a dynamic request is recorded as an orphan root span.The asymmetry is the nasty part: tracing and cancellation work on the blocking path and silently do not on the dynamic path - the highest-traffic, user-facing endpoint. Nobody notices until they need the trace that isn't there.
Discovery
Found during an API-layer design review (comparing the two strategy handlers side by side). Pinned with a regression test that stores a sentinel value on the request context and pre-cancels it, then captures the
context.Contextthe session layer receives:The blocking handler passing, and the dynamic handler failing, on the identical assertion is the whole bug in one screenful.
Fix
Pass
c.Request.Context()at both dynamic call sites, with a comment explaining why the raw gin context must never be used there (so the next handler copies the right pattern).Tests
TestStartDynamicPropagatesRequestContext(names + group paths) andTestStartBlockingPropagatesRequestContextpin the contract on both handlers: request-context values must be visible to the session layer, and a cancelled request must be observed as cancelled. API package tests andgolangci-lintpass.Docs
No user-facing docs impact (no config, label, or API surface change).