Go cookbook · Intermediate
Use context deadlines for outbound calls
A slow dependency makes requests pile up because calls never time out.
Approach
Derive a context with a deadline from the incoming request's context for every outbound call, so calls stop when the client disconnects or the budget runs out. Configure the HTTP client's transport timeouts too.
Solution
package pricing
import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"time"
)
var client = &http.Client{
Transport: &http.Transport{
DialContext: (&net.Dialer{Timeout: 2 * time.Second}).DialContext,
TLSHandshakeTimeout: 3 * time.Second,
ResponseHeaderTimeout: 5 * time.Second,
MaxIdleConnsPerHost: 20,
},
}
type Quote struct {
PriceCents int `json:"price_cents"`
}
func FetchQuote(ctx context.Context, sku string) (Quote, error) {
ctx, cancel := context.WithTimeout(ctx, 800*time.Millisecond)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://pricing.internal/quotes/"+sku, nil)
if err != nil {
return Quote{}, err
}
resp, err := client.Do(req)
if err != nil {
return Quote{}, fmt.Errorf("fetch quote %s: %w", sku, err) // includes context.DeadlineExceeded
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return Quote{}, fmt.Errorf("fetch quote %s: status %d", sku, resp.StatusCode)
}
var q Quote
return q, json.NewDecoder(resp.Body).Decode(&q)
}
// In a handler: quote, err := FetchQuote(r.Context(), sku)
Pitfalls
- Using
context.Background()inside handlers, so work continues after the client has gone. - Forgetting
defer cancel(), leaking timers until the parent context ends. http.DefaultClientin production: no timeouts at all.
Variations
errors.Is(err, context.DeadlineExceeded)distinguishes timeouts for metrics and fallbacks.- Use
context.WithoutCancel(ctx)for work that must finish after the request (audit logs).
Further reading: context package
Reference: https://pkg.go.dev/context
More Go recipes
- Benchmark code with testing.B.Loop
- Build a small, secure container image for a Go service
- Decode and validate JSON request bodies safely
- Embed static files and templates in the binary
- Load and validate configuration from environment variables
- Log structured JSON with slog and redact secrets
- Process jobs concurrently with a bounded worker pool
- Profile a running service with pprof