Context Package
The context package carries deadlines, cancellation signals, and request-scoped values across API boundaries and goroutines.
Introduction
The context package carries deadlines, cancellation signals, and request-scoped values across API boundaries and goroutines. Every HTTP handler receives r.Context(); database and RPC calls should accept ctx context.Context as first parameter.
context.WithCancel, WithTimeout, and WithDeadline create derived contexts. Never store contexts in structs — pass explicitly. Context values should be request-scoped only (trace IDs), not optional parameters.
Production outages from ignoring context cancellation are common — this lesson teaches propagation patterns used at Google and every major Go shop.
The story
When Kubernetes sends SIGTERM to a pod, your Go HTTP server receives context.Canceled on in-flight requests — ctx.Done() signals goroutines to stop gracefully within the 30-second termination window. Google's gRPC stack threads context.Context through every RPC for deadlines, cancellation, and request-scoped values like trace IDs.
Pass context.Context as the first parameter to every function that might block — ignoring cancellation leaks goroutines and delays rolling deploys.
Understanding the topic
Key concepts
- context.Background() root; context.TODO() placeholder.
- WithCancel returns ctx and cancel func — call cancel to propagate.
- WithTimeout/WithDeadline auto-cancel after duration.
- ctx.Done() returns channel closed on cancellation.
- ctx.Err() returns context.Canceled or context.DeadlineExceeded.
- Value keys should be unexported type to avoid collisions.
Step-by-step explanation
- Derive child context from parent; cancel child affects descendants.
- Pass ctx as first param: func Query(ctx context.Context, ...).
- Select on ctx.Done() in long loops and blocking ops.
- HTTP server cancels ctx when client disconnects.
- grpc and database drivers respect ctx cancellation.
- cancel() releases resources — defer cancel() after WithCancel.
Practical code example
HTTP handler passing context to service with timeout:
package mainimport ("context""fmt""net/http""time")type Service struct{}func (s *Service) FetchUser(ctx context.Context, id string) (string, error) {select {case <-time.After(100 * time.Millisecond):return fmt.Sprintf("user-%s", id), nilcase <-ctx.Done():return "", ctx.Err()}}func handler(svc *Service) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {ctx, cancel := context.WithTimeout(r.Context(), 50*time.Millisecond)defer cancel()user, err := svc.FetchUser(ctx, r.URL.Query().Get("id"))if err != nil {http.Error(w, err.Error(), http.StatusGatewayTimeout)return}fmt.Fprintln(w, user)}}func main() {svc := &Service{}http.HandleFunc("/user", handler(svc))fmt.Println("listening :8080")http.ListenAndServe(":8080", nil)}
Line-by-line code explanation
ctx, cancel := context.WithTimeout(parent, 5*time.Second)creates a deadline-bound context.defer cancel()releases timer resources — always call cancel to prevent leaks.ctx.Done()returns a channel closed when the context is canceled or timed out.select { case <-ctx.Done(): return ctx.Err() }exits early on cancellation.context.WithCancel(parent)allows manual cancellation via the returnedcancel()function.context.WithValue(ctx, key, val)carries request-scoped metadata — use unexported key types.http.NewRequestWithContext(ctx, ...)propagates cancellation to outbound HTTP calls.never store Context in structs— pass it explicitly through the call chain.
Key takeaway: Always defer cancel(). Shorter timeout on derived ctx. Propagate r.Context() from HTTP into service layer.
Real-world use
Where you'll use this in production
- Cancelling database queries when HTTP client disconnects.
- Distributed trace ID propagation via context values.
- Graceful shutdown cancelling in-flight requests.
- RPC deadline propagation in microservice chains.
Best practices
- Context as first parameter in all I/O functions.
- defer cancel() immediately after WithCancel/Timeout.
- Don't use context values for optional func parameters.
- Honor ctx.Done() in all loops and external calls.
- Set reasonable timeouts on outbound HTTP and DB calls.
- Use context.WithoutCancel only when explicitly needed.
Common mistakes
- Storing context in struct fields.
- Ignoring ctx in database/http calls — goroutine leaks.
- Forgetting defer cancel() — timer leak until parent done.
- Using string keys for context values — collision risk.
- Passing nil context — use Background or TODO, never nil.
Advanced interview questions
Q1Beginnercontext.Background vs TODO?
Q2BeginnerWhen context cancels?
Q3IntermediateStore context in struct?
Q4IntermediateHTTP request context cancellation?
Q5AdvancedDesign context propagation across 3 microservices.
Summary
context propagates cancellation, deadlines, and request values. Pass ctx as first parameter; defer cancel() on derived contexts. HTTP handlers must propagate r.Context() to downstream calls. Never store context in structs or use for optional params. Next lesson: file handling with os and io packages.