Go's `context` Package: Propagating Cancellation and Deadlines
Go's `context` package is a lifeline for requests, carrying cancellation signals, deadlines, and values across function calls and goroutines. It's essential for I/O-bound operations to prevent resource leaks.
WHY IT EXISTS: In concurrent systems, it's easy to start work in a goroutine that outlives the request that spawned it. This leads to resource leaks and wasted CPU cycles. The context package was created to provide a standard, language-level way to manage the lifecycle of a request and its associated work.
THE MENTAL MODEL: Think of a Context as a control wire threaded through your function calls. It carries three signals: a cancellation flag ('stop what you're doing'), a deadline ('stop by this time'), and a bag of request-scoped values. When you cut the wire at any point by canceling it, the signal propagates down to all derived wires, telling all downstream work to stop.
HOW IT WORKS: You start with a root context, context.Background(). To add a signal, you create a derived context using functions like WithCancel, WithTimeout, or WithValue. These return a new child Context and, for cancellation or deadlines, a CancelFunc. You pass the child context down the call stack. To signal cancellation, you call the CancelFunc. Any code that receives a context should listen for its cancellation by checking the ctx.Done() channel. When a parent context is canceled, all contexts derived from it are also canceled.
WHEN TO USE IT: Use Context as the first parameter in any function that might perform I/O, wait for a long time, or run in a separate goroutine. This includes HTTP handlers, database clients, and RPC calls. It is the standard way to handle timeouts and cancellations in modern Go.
WHEN NOT TO USE IT: Do not store a Context inside a struct; pass it explicitly as the first argument to a function. Never pass a nil context; use context.TODO() if you are unsure which context to use. Do not use context.WithValue to pass optional parameters to a function; it's meant for request-scoped data that needs to cross API boundaries, like a request ID or trace information.
ONE CANONICAL EXAMPLE: A web server receives a request and creates a context with a 500ms timeout using context.WithTimeout. It passes this context to a function that makes a database query. If the query takes longer than 500ms, the context is automatically canceled. The database driver, which listens on ctx.Done(), sees the cancellation and aborts the query, returning an error instead of hanging indefinitely.
Read the original → pkg.go.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.