📖 Study Material – Context, Timeouts & Cancellation
🧠 1. Why Context Exists
In real-world systems, operations often:
- Take time (API calls, DB queries)
- Depend on other services
- May need to be stopped midway
Without control:
- Requests hang forever ❌
- Resources get exhausted ❌
- Systems become unstable ❌
Go introduces context.Context to solve this.
👉 It provides:
- Cancellation
- Timeouts
- Deadlines
- Request-scoped values (limited use)
🆚 Python vs Go Mental Model
| Python | Go |
|---|---|
| Implicit request handling | Explicit context passing |
| Threading / async | Goroutines + channels |
| No standard cancellation pattern | Built-in context system |
👉 In Go, you must explicitly pass control signals.
📦 2. What is context.Context?
context.Context is an interface used to:
- Signal cancellation
- Set deadlines
- Carry metadata across API boundaries
Core Methods
type Context interface {
Done() <-chan struct{}
Err() error
Deadline() (deadline time.Time, ok bool)
Value(key interface{}) interface{}
}
⚙️ 3. Creating Contexts
3.1 Root Contexts
ctx := context.Background()
Used for:
- Main functions
- Initialization
- Tests
3.2 Cancelable Context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
👉 Use when:
- You want manual control to stop execution
3.3 Timeout Context
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
👉 Automatically cancels after timeout
3.4 Deadline Context
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Second))
defer cancel()
🚦 4. Listening for Cancellation
Always check:
select {
case <-ctx.Done():
fmt.Println("Operation cancelled:", ctx.Err())
return
default:
// continue work
}
🔁 5. Context Propagation
Context must be passed down the call chain:
func process(ctx context.Context) {
fetchData(ctx)
}
func fetchData(ctx context.Context) {
// use ctx here
}
👉 Rule:
Always pass context as the first parameter
⚠️ 6. Common Mistakes (Very Important)
❌ Ignoring cancellation
Bad:
time.Sleep(5 * time.Second)
Good:
select {
case <-time.After(5 * time.Second):
case <-ctx.Done():
return
}
❌ Storing context in struct
Bad:
type Service struct {
ctx context.Context
}
Good:
func (s *Service) Process(ctx context.Context) {}
❌ Passing nil context
Always use:
context.Background()
❌ Forgetting cancel()
Leads to:
- Memory leaks
- Goroutine leaks
🌐 7. Context in HTTP Servers
In Go HTTP:
func handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
}
👉 This context:
- Cancels when client disconnects
- Carries request lifecycle
🔄 8. Real Example – API Call with Timeout
func fetch(ctx context.Context) error {
select {
case <-time.After(3 * time.Second):
fmt.Println("Completed")
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
err := fetch(ctx)
fmt.Println(err)
}
👉 Output:
context deadline exceeded
🧪 9. Testing Context-Based Code
Use timeout contexts:
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
Test:
- Cancellation paths
- Timeout behavior
- Error handling
🧩 10. When to Use Context
Use context when:
- Making HTTP calls
- Calling databases
- Running goroutines
- Handling request lifecycles
Do NOT use context for:
- Passing business data
- Optional parameters
- Configuration
🧠 Key Takeaways
- Context is mandatory in production Go
- Always pass it explicitly
- Respect cancellation signals
- Use timeouts to protect systems
- Keep it simple and idiomatic
🚀 What’s Next?
Now move to: 👉 Exercises
Practice is where context truly “clicks”.