Closed
Description
@Sajmani and I are interested in adding golang.org/x/net/context
to the standard library, probably as the top-level package context
.
(Why not net/context
? It has little to do with networking, except that people often use it with networking; it doesn't even cross network boundaries on its own without help.)
Some places in the standard library which could use it:
- https://golang.org/pkg/net/http/#Request -- add a new
Context
field, alongside like the existing (Context.Done-compatible)Request.Cancel
field. It will take precedence overCancel
, and we can make it work for both outbound and inbound requests. For server requests it would useCloseNotifier
's mechanism for c* ancelation. (which for http2 includes the http2-level explicit stream cancelation frame too, also used by gRPC) - https://golang.org/pkg/net/#Dialer -- same as
http.Request
.net.Dialer
also has a Done-compatibleCancel
field, which could be a Context. - https://golang.org/pkg/os/exec/#Cmd could have a new
Context
field to automatically kill+wait the process when a context expires, if the context expires before the child? - https://golang.org/pkg/database/sql/#DB.Query already takes
...interface{}
which can be special-cased to say that if the first parameter is acontext.Context
, it's used for deadlines. Trashy or clever? Maybe both. We could also add parallelDB.QueryContext
which is a bit of a mouthful. - .... (where else?) ....
Open questions:
- do we do something with the
io
package in the same release, or later? i.e. do we add*os.File.IO(context.Context) io.ReadWriteCloser
sort of methods, to curry a context intoio
interfaces? Or wait on that. If it'd be nice to push down cancelation into even OS-level file I/O, but we don't even really do that inside Google withIO
-like methods and pervasive Context usage, since we don't really use*os.File
. So I'm inclined to wait on that. Too many operating systems to deal with.
Concerns:
- we can't add it to as many places in the standard library we'd like to since APIs are frozen. Basically we can only add it to structs. While we've told people not to add contexts to structs, I think that guidance is over-aggressive. The real advice is not to store contexts. They should be passed along like parameters. But if the struct is essentially just a parameter, it's okay. I think this concern can be addressed with package-level documentation and examples.
- what field name in structs to use? We use "ctx" for variables, but struct names are typically spelled out. But does that cause too much stutter? e.g.
package http
type Request struct {
Context context.Context
}
Metadata
Metadata
Assignees
Type
Projects
Relationships
Development
No branches or pull requests
Activity
elithrar commentedon Mar 5, 2016
For others following, there was a substantial discussion on golang-dev about this as it related to
net/http
: https://groups.google.com/forum/#!topic/golang-dev/cQs1z9LrJDUAs for naming: the only stutter is in the field name in the source. Package users are going to call
r.Context.WithValue
orr.Context.Done
, which has the benefit of being explicit and (say you point out, Brad) retains thectx
naming convention for variables.Looking forward to this happening!
atdiar commentedon Mar 5, 2016
Thinking about http.Handler, people can alternatively rewrite a different kind of handler that has a different signature for ServeHttp. The advantage is that one can easily add a Context parameter.
But also, add return values. For instance a boolean sentinel value to control whether the handler should be the last one to be executed. Or a different http.ResponseWriter (useful if you wrap the ResponseWriter in one of your handlers, for instance when you gzip things, but not only)
The top level mux object that encapsulates the whole request dispatching logic can still remain a regular http.Handler (and the context.Background would be instantiated in its ServeHttp(w,r) and passed to the handler ServeWithCtx(ctx,w,r)). Probably better that I upload an example of what I mean.
There are also a few things that I am not very fond of. context.TODO is one of them. But I don't know if it is too late to change things or not.
Is this the right place to discuss about API design ?
jimmyfrasche commentedon Mar 5, 2016
I think naming it Context in structs is good. There's a stutter, but only for the author of the API. Couldn't be clearer for the users of the API.
Would it be possible to write a go vet check for obvious misuse of a Context in a struct? If so, it would be great to roll that out at the same time. Posting a sign next to the pool is good, but a lifeguard is better.
As much as I'd love Context to be deeply baked into the standard library, I'd be happy with it simply being added to the standard library to say "this is blessed, find good ways to use it" and later adding the best ideas to the stdlib where they can be and kept in third party packages where they cannot. However, I'm not opposed to any of the suggested integrations.
Having the first interface{} parameter to Query be a Context is clever, though, so I'd much rather have the explicit QueryContext.
riannucci commentedon Mar 5, 2016
This is maybe a bit out of place for this particular issue, but I think it would be worth considering a different Context implementation (strawman benchmark) before it gets included in the stdlib.
In that benchmark, I compare a bogus map+lock "caching" context implementation to the default implementation. Even with as few as 10 items, there's a noticeable improvement (%-wise, not in absolute terms. I realize we're talking nanoseconds here) on lookups into the cached version. For contexts deeper than 10 items, there's a noticeable improvement. The construction overhead is similar, but I think with a non-bogus implementation the map version could be even faster for overhead than the linked list one.
I think there's an opportunity to have Context be a *struct-with-hidden-fields instead of an interface, which would afford the opportunity to build a faster implementation of it (e.g. by having .WithValue be a struct method instead of a package method that can be smart about chaining, or a .WithValues to avoid the linked-list-building overhead when adding multiple items to the context at the same time) and by possibly adding a .Compact method to compactify an existing Context to make subsequent lookups fast. It's not possible to build these things in a clean fashion with the current interface-based implementation (since the only way for the context to see up the chain is to actually do lookups, and the only way to know the lookup keys is to wait for some user code to provide them).
I'm mostly thinking about the pattern of:
Which, I think, is a pretty common pattern (I don't have data on hand for this assumption, however).
In general, for most stdlib-type libraries, there would simply be competition for the best implementation. But I think that the community is rallying around a single Context implementation (because it's THE way to build composable/stackable libraries which are loosely coupled). There can't really be an alternate implementation that doesn't stem from the stdlib (or
x/
) that has any hope for survival (since everything needs to adopt the stdlib one to actually be used by anything). To be clear: I think it's GOOD for the community to standardize here, I just think it might be worth considering a slightly speedier implementation before cooking it into the1.x
API compatibility guarantee.Note that the
WithTimeout
andWithDeadline
package functions are well-served by the interface implementation right now: it allows e.g. mocking of the clock. Some similarly mockable interface would need to be built for that functionality, but AFAIK that mocking requirement doesn't really apply to WithValue.edit: typo
kr commentedon Mar 5, 2016
How do you expect putting context in http.Request (or any other parameter-ish struct) will interact with adding values (or timeouts or whatever) to the context?
I can think of two basic approaches, caller-save and callee-save:
or
Of course, it's fine if both caller and callee save the old value, but it's bad if neither of them does it, each expecting the other to do so. So, should we recommend one and document and promote it up front?
rakyll commentedon Mar 5, 2016
Traditionally, context objects should carry configuration. The x/net/context package extends context objects' responsibilities by providing constructs for timeout and cancellation signals. In the scope of stamp coupling of request handlers, it is a valuable addition. But I am not sure how it will scale to the rest of the standard library and furthermore whether the context may end up being abused for signaling purposes. The two cases you mention above (exec.Cmd and sql.DB.Query) does this by solely using a context object for handling signals even though there is no necessity for storage. Are you trying to solve the issue of how data is passed among functions or also trying to have primitives around how the entire execution could be signaled from outside?
One of the concerns I would add to the list is that the context should be scoped to a function call.
"Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx" (link)
If the standard library won't be agreeing with this due to compatibility reasons, don't you think it will create confusion?
bradfitz commentedon Mar 6, 2016
@rakyll, I addressed that final concern in my first post. ("While we've told people...")
zippoxer commentedon Mar 7, 2016
If there's any way to add context to the standard library without making http or net aware of context (can still adjust them to play well with it though), this would be the way to not overwhelm newcomers reading the docs.
okdave commentedon Mar 7, 2016
The plan @bradfitz is proposing makes http and net aware of context, but not in a way that requires their use for basic cases. The http package is already very large, but you only need to use a very small fraction of it to achieve "hello world"-esque examples. Adding context to these packages won't harm newcomers, and should make life easier in more complicated/large codebases.
bradfitz commentedon Mar 8, 2016
I mailed out https://golang.org/cl/20346 &https://golang.org/cl/20347
The first copies x/net/context to the standard library. The second reworks x/net/context to just use the standard library's version.
59 remaining items
minux commentedon May 5, 2016
bradfitz commentedon May 5, 2016
@minux, no rush. Eventually. Maybe at Go 1.8 time.
phuslu commentedon May 8, 2016
@riannucci
I share you concern about the
WithValue
linked-list performance.Now I use below pattern in my codes,
Do you think it could help the looking up performance or not?
Thomasdezeeuw commentedon May 9, 2016
@phuslu instead of guessing, why not write a benchmark?
phuslu commentedon May 9, 2016
@Thomasdezeeuw Thank you.
But the more important thing(I think) is, Does this pattern is the RIGHT practice of context.Context ?
As you see, the original context.Context is used as a nested structure and seems that each node is immutable(Please correct me if I misunderstand). the original common pattern is,
And the pattern which I used is treat context.Context as a mutable object -- although it may has potential performance improvement.
It will encourage people use below code against context.Context
It smells bad. Hopefully could get comments from you and others.
bradfitz commentedon May 9, 2016
@phuslu @riannucci @Thomasdezeeuw, please move discussion of Context.WithValue performance to a new bug.
sonatard commentedon Jul 8, 2016
Hello!!
I have a question about sharing values between middlewares.
I think we'll be able to share values between middlewares in the following code by go 1.7.
But
r.WithContext(ctx)
exec shallow copy for goroutine. I need not shallow copy for only sharing values. It is slow. right? I want to set context to Request. But now it cannot.Please tell me best way sharing values between middlewares using only net/http and context. I think we should not implement context wrapper like 3rd party framework impl for only sharing values.
davecheney commentedon Jul 8, 2016
@sona-tar We don't the issue tracker to ask questions. Please see https://golang.org/wiki/Questions for good places to ask. Thanks.
sonatard commentedon Jul 14, 2016
@davecheney Thx. I moved here. https://forum.golangbridge.org/t/share-values-between-middlewares-on-net-http/2952