Go

How is Value lookup implemented, and what are its performance implications?

Question 269HardGo 1.22 to 1.25

Every WithValue call wraps its parent in a valueCtx{Context; key, val any} that stores one pair. Value(k) is a linked-list walk: check this node's key, then move to the parent, until it reaches Background/TODO, which returns nil. Each lookup therefore costs O(depth), and a miss always walks the whole chain.

Implications:

  • Deep middleware stacks that each add values (and cancel layers, which also forward Value) make every lookup slower. Usually this doesn't matter, but it does in hot paths that read a value per operation.
  • Don't call ctx.Value in a tight loop. Read it once into a local variable.
  • To attach many related fields, store one struct pointer instead of many separate WithValue layers.
type reqMeta struct {
	RequestID string
	TenantID  string
	Start     time.Time
}
type metaKey struct{}

func withMeta(ctx context.Context, m *reqMeta) context.Context {
	return context.WithValue(ctx, metaKey{}, m) // one node, not three
}

Gotcha: values in context should be immutable or safe for concurrent use, because the same context is often shared across goroutines. Storing a pointer to a struct you later change without synchronization is a data race.

More on Context

All 35 Context questions