What is Context?

In Go, context.Context is an interface which defined in package context that carries deadlines, cancellation signals, and request-scoped values across API boundaries and between goroutines.
Incoming requests to a HTTP server creates a Context, and outgoing calls to servers accept a Context. The chain of function calls between them MUST propagate the Context(This is developer's responsibility)
Usage of Context Interface:
1. Cancellation signals: Cancel work when a client disconnects or a timeout expires
2. Deadlines: Set deadlines for operations (e.g: DB query must finish in 5 seconds)
3. Request-scoped values: Pass request-scoped data (e.g., user IDs, authentication tokens) through a call chain

go provided Context Interface

go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.2.linux-amd64/src/context/context.go
type emptyCtx struct{}

//// Context Interface
type Context interface {
  Deadline() (deadline time.Time, ok bool)
  Done() <-chan struct{}
  Value(key any) any
}

// Background is never canceled, has no values, deadline
type backgroundCtx struct{ emptyCtx }
func Background() Context {
	return backgroundCtx{}
}

type CancelFunc func()
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	c := withCancel(parent)
	return c, func() { c.cancel(true, Canceled, nil) }
}

func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc) {
	c := withCancel(parent)
	return c, func(cause error) { c.cancel(true, Canceled, cause) }
}

import (
	"context"
)
func main() {
  ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
}
      

How Context is provided in Go?

http.Server creates request with context (r.Context()). You can:
- Ignore it: and find problems listed here
- Or can handle and pass to the function (as shown below)


func AddLead(db *sql.DB, w http.ResponseWriter, r *http.Request) {
    a := r.Context()
    createLead(db, a)       // You can pass context downstream
}
func createLead(db *sql.DB, ctx context.Context) {
    query := `
    INSERT INTO leads (name, email, phone, whatsapp)
    VALUES ($1, $2, $3, $4,)
    RETURNING id;`

    // Add timeout: query must finish in 5 seconds
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()

    // Do DB query with context
    err := db.QueryRowContext(ctx, query, ...).Scan(&id)
}
      

Problems without Context

Database Operations

1. DB write path without Context can hang when:
- DB is slow (network issue, lock): INSERT Query blocks for minutes, your HTTP handlerblocks, frontend client waits
- Frontend Client disconnects: and now work needed to be done still DB INSERT Query still runs, wastes DB connection, CPU
- DB server crashes: INSERT/SELECT Query hangs forever, no timeout
- Server gets 1000 requests: 1000 DB connections are opened and blocked, server becomes unresponsive

Problematic Code Solution

func AddLead(db *sql.DB) (int, error) {
    query := `INSERT INTO leads ... RETURNING id;`
    err := db.QueryRow(query, ...).Scan(&id)  // ← NO CONTEXT
    .. 
}
                

func AddLead(db *sql.DB, ctx context.Context) (int, error) {
    query := `INSERT INTO leads ... RETURNING id;`
    
    // Add timeout: query must finish in 5 seconds
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()
    
    // Pass context to DB call
    err := db.QueryRowContext(ctx, query, ...).Scan(&id)
    return id, err
}

2. DB Read path with Context can hang
- When DB is slow (network issue, lock) due to no timeout set on DB read path
- DB server crashes: SELECT Query hangs forever, no timeout

2. Server keeps waiting, while client disconnected

HTTP Client connects and then disconnects (within 5 seconds)
Go's `http.Server` spawns 1 Go Routine per request to handle every client. request. Without Context Http Server will waiting for 180+ seconds, when at 180 seconds TCP keepalive timer fires(if set) then it close the connection. This is server resource wastage.


// WITHOUT context timeout
client → HTTP request → server starts DB query → client waits 5s → client timeouts → 
server STILL runs DB query for 175 more seconds → wastes DB connection, CPU

// WITH context timeout:
client → HTTP request → server sets 5s timeout → client waits 5s → client timeouts → 
server's DB query is CANCELLED immediately → frees DB connection, CPU