context
to help us manage long-running processes.Server
takes a Store
and returns us a http.HandlerFunc
. Store is defined as:store
's Fetch
method to get the data and writes it to the response.Store
which we use in a test.Store
can't finish aFetch
before the user cancels the request.Store
to cancel the work so update the interface.data
and a way of knowing it has been told to cancel. We'll also rename it to SpyStore
as we are now observing the way it is called. It'll have to add Cancel
as a method to implement the Store
interface.The context package provides functions to derive new Context values from existing ones. These values form a tree: when a Context is canceled, all Contexts derived from it are also canceled.
cancellingCtx
from our request
which returns us a cancel
function. We then schedule that function to be called in 5 milliseconds by using time.AfterFunc
. Finally we use this new context in our request by calling request.WithContext
.Store
before we fetch on every request.context
has a method Done()
which returns a channel which gets sent a signal when the context is "done" or "cancelled". We want to listen to that signal and call store.Cancel
if we get it but we want to ignore it if our Store
manages to Fetch
before it.Fetch
in a goroutine and it will write the result into a new channel data
. We then use select
to effectively race to the two asynchronous processes and then we either write a response or Cancel
.*testing.T
when creating the spy.Store
? What if Store
also happens to depend on other slow-running processes? We'll have to make sure that Store.Cancel
correctly propagates the cancellation to all of its dependants.context
is that it is a consistent way of offering cancellation.Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context. The chain of function calls between them must propagate the Context, optionally replacing it with a derived Context created using WithCancel, WithDeadline, WithTimeout, or WithValue. When a Context is canceled, all Contexts derived from it are also canceled.
At Google, we require that Go programmers pass a Context parameter as the first argument to every function on the call path between incoming and outgoing requests. This allows Go code developed by many different teams to interoperate well. It provides simple control over timeouts and cancelation and ensures that critical values like security credentials transit Go programs properly.
context
to our Store
and let it be responsible. That way it can also pass the context
through to its dependants and they too can be responsible for stopping themselves.Store
and that it handles the error that will come from the Store
when it is cancelled.Store
interface to show the new responsibilities.SpyStore
context
.data
channel. The goroutine listens for the ctx.Done
and will stop the work if a signal is sent in that channel.select
to wait for that goroutine to finish its work or for the cancellation to occur.context
so make sure you understand what's going on.httptest.ResponseRecorder
doesn't have a way of figuring this out so we'll have to roll our own spy to test for this.SpyResponseWriter
implements http.ResponseWriter
so we can use it in the test.context
and relies on the downstream functions to respect any cancellations that may occur.context
and uses it to cancel itself by using goroutines, select
and channels.http.ResponseWriter
if you need it.If you use ctx.Value in my (non-existent) company, you’re fired
context
as it feels convenient.context.Values
is that it's just an untyped map so you have no type-safety and you have to handle it not actually containing your value. You have to create a coupling of map keys from one module to another and if someone changes something things start breaking.context.Value
. This makes it statically checked and documented for everyone to see.The content of context.Value is for maintainers not users. It should never be required input for documented or expected results.
context
everywhere is a smell, that it's pointing to a deficiency in the language in respect to cancellation. He says it would better if this was somehow solved at the language level, rather than at a library level. Until that happens, you will need context
if you want to manage long running processes.