This is a creation in Article, where the information may have evolved or changed.
This is a experience report on the use of, and difficulties with, the context.Context
facility in Go.
Many authors, including myself, has written about the use of, misuse of, and how they would, in context.Context
a future ITE Ration of Go. While opinions differs in many aspects context.Context
of, one thing is clear–there are almost unanimous agreement that the Context.WithValue
met Hod on the context.Context
interface was orthogonal to the type's role as a mechanism to control the lifetime of request scoped Resou RCEs.
Many proposals has emerged to address this apparent overloading of with context.Context
a copy on write bag of values. Most approximate thread local storage so is unlikely to is accepted on ideological grounds.
This post explores the relationship between context.Context
and lifecycle management and asks the question, is attempts to fix Solving the wrong problem?
Context is a request scoped paradigm
The documentation for the package strongly recommends and that's only for context
context.Context
request scoped values:
Do not store contexts inside a struct type; Instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named CTX:
Func dosomething (CTX context. Context, arg arg) error { //...}
Specifically context.Context
values should only live in function arguments, never stored in a field or global. This makes applicable only to the context.Context
lifetime of resources in a request ' s scope. Given Go ' s lineage on the server, this is a compelling use case. However, there exist other use cases for cancellation where the lifetime of the resource extends beyond a single request. For example, a background goroutine as part of an agent or pipeline.
Context as a hook for cancellation
The stated goal of the package is context
:
Package context defines the context type, which carries deadlines, cancelation signals, and other request-scoped values AC Ross API boundaries and between processes.
Which sounds great, but belies its catch-all nature. is context.Context
used in three independent, yet sometimes conflated, scenarios:
- Cancellation via
context.WithCancel
.
- Timeout via
context.WithDeadline
.
- A bag of values via
context.WithValue
.
At any point, a context.Context
value can represent any one, or all three of these independent concerns. However, context.Context
' s most important facility, broadcasting a cancellation signal, was incomplete as there is no-to- wait for the signal-be acknowledged.
Looking to the past
As this is a experience report, it would are germane to highlight some actual experience. In Gustavo Niemeyer wrote a package for Goroutine lifecycle management called which are used by Juju for the man tomb
Agement of the worker goroutines within the various agents in the Juju system.
tomb.Tomb
S is concerned only with lifecycle management. Importantly, this is a generic notion of a lifecycle, not tied exclusively to a request, or a goroutine. The scope of the resource ' s lifetime is defined simply by holding a reference to the tomb value.
A tomb.Tomb
value has three properties:
- The ability to signal the owner of the "Tomb to shut" down.
- The ability to wait until this signal has been acknowledged.
- A-to capture a final
error
value.
However, tomb.Tomb
S has one drawback, they cannot be shared across multiple goroutines. Consider this prototypical network server where a tomb.Tomb
cannot replace the use of sync.WaitGroup
.
Func Serve (l net. Listener) Error { var wg sync. Waitgroup var conn net. Conn var err error for { Conn, err = l.accept () if err! = nil {break } WG. ADD (1) go func (c net. Conn) { defer WG. Done () handle (c) } (conn) } WG. Wait () return err}
To is fair, context.Context
cannot do this either as it provides no built in mechanism to acknowledge cancellation. What's needed is a form sync.WaitGroup
of this allows cancellation, as well as waiting for it participants to call wg.Done
.
Context should become, well, just context
The purpose of the context.Context
type is in it ' s name:
context/kɒntɛkst/ noun
The circumstances that form the setting for a event, statement, or idea, and in terms of which it can be fully understood .
I propose context.Context
becomes just that; a request scoped association list of copy on write values.
Decoupling lifetime management from as context.Context
a store of request scoped values would hopefully highlight that request Contex T and lifecycle management are orthogonal concerns.
Best of all, we don't need to wait til Go 2.0 to explore these ideas like Gustavo ' s package tomb
.