Context and cancellation
Propagate timeouts and shutdown signals through every layer of a Go service.
Table of Contents
Goroutines without cancellation leak. Context is Go’s answer: a value you pass down the call chain that carries deadlines, cancel signals, and request-scoped data.
The pattern
func handler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()
data, err := fetch(ctx, "https://example.com")
if err != nil {
http.Error(w, "upstream timeout", http.StatusGatewayTimeout)
return
}
w.Write(data)
}
func fetch(ctx context.Context, url string) ([]byte, error) {
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
Rules that keep you out of trouble
ctxis the first parameter, namedctx, always- Call
cancel()—defer cancel()the moment you create a derived context - Never store contexts in structs; pass them explicitly
- Select on
ctx.Done()inside loops that can run long
Combined with bounded channels from part 1, this is the foundation of services that shut down cleanly.