# Context and cancellation

> One value that carries a deadline and a cancel signal through every call a request makes.

Concept · Go · https://levelcode.org/concepts/context-cancellation

A request handler might query a database, call two services and start a goroutine. If the client disconnects or the deadline passes, all of that work should stop. Go's answer is `context.Context`: a value passed as the **first argument** of every function that does I/O. It carries:

- a **cancel signal**, through `ctx.Done()`, a channel that closes when the work should stop;
- a **deadline**, which fires that signal automatically;
- the **reason**, from `ctx.Err()`, which is `context.Canceled` or `context.DeadlineExceeded`;
- a few **request-scoped values**, like a trace ID. Use these sparingly.

## Deriving and passing

Contexts form a tree. A child gets cancelled when its parent does, and it can only tighten the deadline, never extend it.

```go
func (h *Handler) GetOrder(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second) // cancelled if client leaves, or after 2s
	defer cancel()                                                // always: releases the timer

	order, err := h.store.GetOrder(ctx, id)         // database/sql and pgx honour ctx
	if errors.Is(err, context.DeadlineExceeded) { /* 504 */ }
	// ...
}
```

The standard library and good clients (`net/http`, `database/sql`, pgx, gRPC) watch the context and abort when it's done. Your own long loops and goroutines have to check it themselves:

```go
for job := range jobs {
	select {
	case <-ctx.Done():
		return ctx.Err()
	default:
	}
	process(job)
}
```

## Rules

- **Pass it along explicitly; don't store it in a struct.** Whatever a function does on the request's behalf should use the request's context.
- **Always `defer cancel()`.** Skipping it leaks the context's resources until the parent is cancelled.
- **Never pass `nil`.** Use `context.Background()` at the top of `main` or a background job, and `context.TODO()` while you're refactoring.
- **A goroutine that ignores its context leaks.** If the handler returns and nobody reads the goroutine's channel or cancels its work, it lives forever. Give every goroutine a way to stop.
- **Use `context.WithoutCancel(ctx)`** (Go 1.21+) for work that must finish even if the request is cancelled, such as writing an audit record. It keeps the values but drops the cancellation.

This is how Go implements [deadline propagation](https://levelcode.org/concepts/timeouts), and the same signal drives [graceful shutdown](https://levelcode.org/concepts/graceful-shutdown).
