Exploring the context package
I think you should be using the context package in your next Go project.
This is a short talk, so I’ll just cover some key points with running examples.
Note: if you’re reading this as a blog post, you’ll need to be a little familiar with go and the command line.
Package context defines the Context type, which carries deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes.
The context package was created by Brad Fitzpatrick. Before it became public, it was already used internally at Google ‘as the first parameter for all functions’. It first surfaced to the public as an external package, and was then introduced into the standard library in Go1.7, when it was integrated into several standard-libary packages — notably as a member of the http.Request type, and similarly in other packages.
At Hapara we have used context.WithDeadline to protect one of our services from overloading — it provides a basic kind of back-pressure.
The introduction of the Context type, as a member of the Request object, is significant — the standard library now provides features previously supplied by third party packages, such as negroni (and similar), and Gorilla Context.
- Install the examples with `go get github.com/laher/context-example`
- Run the code with
go run *.go
- Now invoke the service from a separate terminal, with
curl -i http://localhost:8765/?d=1s
Handling deadlines (timing out)
Invoke the ‘slow query’ with
curl -i http://localhost:8765/?d=4s (note that the service times out after 3 seconds). Backpressure of a sort.
Middleware in Go refers to an http handler which wraps around a multiplexer. There are several 3rd party middleware solutions (such as negroni), but really the standard library supports a very similar pattern. The use of a Context in the request allows us to hold data in the request.
Cancellation and multiple receivers
- Run the ‘cancellation’ example with
curl -i http://localhost:8765/cancel?d=3s
- Context.Done() uses a closed channel to ensure that multiple goroutines can receive the same ‘done’ message.
The Context can store request-scoped variables. It’s useful when writing ‘middleware’, but it’s a little bit ‘anti-pattern’ — it’s a bit magical, because it’s not type-safe. See ‘client ip handling’ for an example of how to encapsulate the casting.
Client disconnection triggers ‘Done’ (since go1.8)
- Run the code using Go1.8.
curl -i http://localhost:8765/?d=3s
- Cancel (Cmd-C or Ctrl-C) the curl comand prematurely.
- Observe that the context is cancelled immediately.
Bug-catching - unhandled deadlocks?
curl -i http://localhost:8765/buggy. See how the server hangs. Nothing is pulling off the channel. Eek!
- Amend the invocation of WrapContext to use `WrapContextWithHijack instead.
- Now run
curl -i http://localhost:8765/buggyagain. See how the service sends a response and closes the connection.
- Hold on, it’s not being terminated correctly. Fix me.
Thanks, that’s all for now. Hopefully it’s given you some ideas about how you might use the context package.