From time to time I am faced with the "Context" concept which, as a rule is created for all incoming requests. Recently I've read the Go blog article that describes using the golang.org/x/net/context
package. However, after playing with the code and trying to reproduce the logic of the article, I still hardly understand how to use it for every incoming request and even why it is useful for this.
How should I organize my code to create context (and what should it contain, generally) for every incoming request using the golang.org/x/net/context
package? Could anybody give a little example and explain what is so useful and why so frequently used?
One of the most common needs for context passing is correlating outgoing requests to incoming requests. I have used this for a variety of purposes, for example:
Many languages and platforms have convenient/magical ways to get the current Http request. C# has HttpRequest.Current
which is globally available (via thread local storage) to anyone who wants to know the context of the current http request. You can set arbitrary data on it to communicate various context data. Other platforms have similar facilities.
Since go has no facilities for goroutine local storage, there is no way to store a global variable in the context of the current http request. Instead, it is idiomatic to initialize the context at the boundary of your system (an incoming request), and pass it as an argument to any downstream components that need access to that information.
One super simple way to do this would be to make a context object with the current http request and pass that around:
func someHandler(w http.ResponseWriter, r * http.Request){
ctx := context.WithValue(context.Background(),"request",r)
myDatabase.doSomething(ctx,....)
}
You can of course limit it to a more targeted set of data you need to pass around rather than the entire request.
The other thing that the context package helps with (and I think that blog does an ok job of pointing out), is a common framework for timeouts or deadlines.
Note that the context package does not enforce timeouts for you. It is up to the components receiving a context object to watch the Done channel and self-cancel their own http request or database call or calculation or whatever.
It is extremely useful to be able to manage timeouts from the outside of a component. If I have a database module, I don't need to hardcode timeout values, just be able to handle a timeout triggered from the outside.
One way I have done this is in a service that makes multiple db / service calls per incoming request. If the total time exceeds 1 second, I want to abort all outbound operations and return a partial or error result. Initializing the context with a timeout at the top level and passing it to all dependencies is a really easy way to manage this.
It is not always pretty for the dependency to listen to the Done channel and abort it's work, but as the blog shows, it is not terribly painful either.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With