Closed
Description
What version of Go are you using (go version
)?
1.10
Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (go env
)?
linux
What did you do?
Consider the following standard HTTP Go server stack:
- Middlewares that extract user credentials and request id from the headers and inject them to the
http.Request
context as values. - The
http.Handler
launches an asynchronous goroutin task which needs those values from the context. - After launching the asynchronous task the handler returns 202 to the client, the goroutin continues to run in background.
Problem Statement
- The async task can't use the request context - it is cancelled as the
http.Handler
returns. - There is no way to use the context values in the async task.
- Specially if those values are used automatically with client roundtrippers (extract the request id from the context and inject it to http headers in a following request.)
Proposal
I suggest to add a function that combines values from two contexts - context.WithValues(ctx, values)
or so.
An initial implementation is available here.
Discussion is available in golang-nuts forum
Metadata
Metadata
Assignees
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
[-]proposal: context: add copy values.[/-][+]proposal: context: add WithValues.[/+][-]proposal: context: add WithValues.[/-][+]proposal: context: add WithValues[/+]rsc commentedon Aug 6, 2018
See also #19643. /cc @Sajmani @zombiezen @bcmills
bcmills commentedon Aug 10, 2018
Some values in the
Context
may have finite lifetimes after which they can no longer be used. (For example, when theEnd
method of an OpenCensusSpan
s is called, it is flushed to theExporter
s, and future annotations to the span will be lost.)In such cases, you need something more than just to carry over the values: you need to filter out values that can no longer be used, or perhaps extend their lifetimes.
That sort of use-case doesn't seem to be addressed in this proposal.
Sajmani commentedon Aug 11, 2018
posener commentedon Aug 11, 2018
@bcmills can you elaborate on the given example, for those who are not familiar with the code - as it seems to me, if there is an explicit call to
End
, you can override values withnil
value. In the case of spans, I think that we will want to record calls, even after the handler was finished? So we could see related propagation of the request even in asynchronous task.And generally, what sort of cases need filtering out values from the context?
bcmills commentedon Sep 7, 2018
Overrides of
context
values are generally scoped to subtrees of the function call tree:context.WithValue
produces an immutable entry in the tree.Right, that's the problem: when
End
is called, theSpan
is flushed out to the log and nothing more can be recorded for it.posener commentedon Sep 10, 2018
I'm sorry, I don't understand how spans are bad example here:
For long running tasks that were created from HTTP handlers - I think it is bad practice to create a new context for them, and it is important for them to know the context that they came from. The examples I gave demonstrate the need, and they don't have a solution currently in the standard library.
bcmills commentedon Sep 13, 2018
The middleware is exactly the point: at some point, that middleware needs to know when to flush the span, and at the moment the only reasonable signal it gets is “the next handler down the chain has returned”.
posener commentedon Sep 14, 2018
Maybe asynchronous task trace should not be the same as the handler itself trace. Maybe if you want to send spans from asynchronous task, you should create a new trace and send it when you are done?
bcmills commentedon Sep 14, 2018
Yes, that's exactly my point: if the trace may be stored and accessed in the
Value
s of aContext
, then it is inappropriate to propagate those values to an asynchronous task.rsc commentedon Oct 11, 2018
This need can be satisfied outside the standard library, as you have. (https://github.com/posener/ctxutil/blob/master/values.go)
There are various things you might want to do to mix parts of two contexts. It's not clear which one is "right" if we're going to have a blessed mixer. Better for users who need a specific kind of mix to implement it themselves. This is why context.Context is an interface.
bcmills commentedon Oct 11, 2018
Note that any
context.Context
implementation outside the standard library imposes an extra goroutine every timecontext.WithCancel
orcontext.WithDeadline
is called on it.If we're going to press for folks to address these sorts of issues outside the standard library, we should probably make the standard library play more nicely with external implementations.