> For the complete documentation index, see [llms.txt](https://bagus-cahyono.gitbook.io/programming-notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bagus-cahyono.gitbook.io/programming-notes/golang/context.md).

# Context

The context package in Go provides a way to manage request-scoped data, deadlines, and cancellation signals across API boundaries and between processes.

It is a must if you want to build robust, production-ready applications in Go, particularly for handling concurrent workloads like HTTP servers, database queries, and background tasks.

Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context. The chain of function calls between them must propagate the Context, optionally replacing it with a derived Context created using `WithCancel`, `WithDeadline`, `WithTimeout`, or `WithValue`. When a Context is canceled, all Contexts derived from it are also canceled.

## Context

`context.Context`

The base interface that provides methods to retrieve deadlines, cancellation signals, and values.

* `Done() <-chan struct{}`: Returns a channel that closes when the context is canceled or times out.
* `Err() error`: Returns the reason for cancellation, if any.
* `Deadline() (time.Time, bool)`: Returns the deadline and whether a deadline is set.
* `Value(key any) any`: Retrieves request-scoped values.

## Functions in `context` Package

### context.WithCancel

```go
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
```

Returns a copy of parent context that can be manually canceled by calling the returned cancel function.

### context.WithDeadline

```go
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
```

Returns a copy of parent context that automatically cancel after deadline exceeded.

### context.WithTimeout

```go
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
```

Returns a copy of parent context that automatically cancels after a specified duration. `WithTimeout` returns `WithDeadline(parent, time.Now().Add(timeout))`.

### context.WithValue

```go
func WithValue(parent Context, key, val any) Context
```

Returns a copy of parent context that carries request-scoped values.

## Best Practices

* **Pass Context Explicitly**:
  * Always pass `context.Context` as the first argument to functions handling operations that may require cancellation or deadlines.
  * Use context to propagate request metadata (e.g., trace IDs) across HTTP handlers or gRPC interceptors.
  * Combine contexts appropriately to propagate cancellations or deadlines.
* **Respect Cancellation**:
  * Periodically check ctx.Done() in long-running operations and exit promptly when canceled.

    ```go
    select {
    case <-ctx.Done():
        return ctx.Err()
    default:
        // Continue processing
    }
    ```
* **Use Timeouts Sparingly**:
  * Avoid overusing `context.WithTimeout` in nested operations to prevent conflicting deadlines. Set timeouts at higher levels and propagate them down.
* **Avoids**:
  * **Using context.WithValue for Critical Data**: Use `WithValue` only for request-scoped data. Avoid storing critical or large data; instead, pass such data explicitly.
  * **Using `context.Background` Everywhere**: Use `context.Background` only for root-level operations (e.g., in main or tests).
  * **Avoid Mutating Values in Context**: Contexts are immutable. Avoid attempts to modify or replace values in context.

## Example

```go
package main

import (
    "context"
    "net/http"
    "time"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
        defer cancel()

        err := doWork(ctx)
        if err != nil {
            http.Error(w, "Request timed out", http.StatusRequestTimeout)
            return
        }
        w.Write([]byte("Success"))
    })

    http.ListenAndServe(":8080", nil)
}

func doWork(ctx context.Context) error {
    select {
    case <-time.After(3 * time.Second): // Simulate long work
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}
```

* In above example we try to simulate long work and it should response with error `Request timed out`.
* Try to make the timeout longer and see what the result is.

## Reference

* <https://pkg.go.dev/context>
