Kinoko's TIL Log

Go Context

The Point

Go’s context is the standard solution for propagating cancellation signals, timeouts, and request-scoped data across goroutines. It lets an entire call chain stop cleanly at a unified point, leaving no goroutine leaks.

Explanation

The problem

An HTTP handler often runs multiple goroutines underneath – hitting the DB, calling external APIs, doing computation. Three scenarios cause trouble:

  1. Timeout: the request times out, and downstream work should stop too
  2. Cancellation: the user closes the browser mid-request; continuing is wasted work
  3. Request-scoped data: auth tokens and trace IDs need to flow down the call chain without adding a parameter to every function

context unifies all three into a single standard interface.


Basic usage

1ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
2defer cancel() // must call this, otherwise it leaks resources
3
4result, err := db.QueryContext(ctx, "SELECT ...")

When the timeout expires, ctx.Done() closes, and all context-aware functions stop automatically.


How to receive it in a function

Functions that accept a context take ctx context.Context as the first parameter – this is a mandatory Go convention.

1func doWork(ctx context.Context) error {
2    select {
3    case <-ctx.Done():
4        return ctx.Err() // context.DeadlineExceeded or context.Canceled
5    case result := <-longOperation():
6        return process(result)
7    }
8}

Four ways to create a context

ConstructorPurpose
context.Background()Root context, used at program entry points
context.TODO()Placeholder when you are not sure what to use yet
context.WithCancel(parent)Manual cancellation
context.WithTimeout / WithDeadlineAuto-cancel when time expires
context.WithValue(parent, key, val)Attach request-scoped values (use sparingly)

Every new context is a child of its parent – cancellation only propagates from parent to child. When the parent is cancelled, all children are cancelled too.

Knowledge Sugar

Context must not be stored in a struct

Always pass it as a parameter, never put it in a struct field.

A context represents the lifetime of a request, not object state. Storing it in a struct makes the lifetime unclear.

WithValue is only for cross-cutting concerns

WithValue is for things like tracing IDs and auth tokens – data every request needs but that would pollute function signatures. Do not use it to pass regular business parameters – for those, just add a proper parameter.

Mental model

Context is a control signal for a tree. When the root says “stop,” every goroutine in the tree stops – cleanly, no leaks.

#go #concurrency #til

← Back to Main Page