Golang Context Package
Fleeting- External reference: https://medium.com/codex/go-context-101-ebfaf655fa95
- External reference: https://btree.dev/golang-context
- External reference: https://go.dev/blog/context-and-structs
- External reference: https://go.dev/blog/context
- External reference: https://pkg.go.dev/context
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
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 { // … use ctx … }
context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions
same Context may be passed to functions running in different goroutines; Contexts are safe for simultaneous use by multiple goroutines.
When a request is canceled or times out, all the goroutines working on that request should exit quickly so the system can reclaim any resources they are using
context package that makes it easy to pass request-scoped values, cancellation signals, and deadlines across API boundaries to all the goroutines involved in handling a request
Background is the root of any Context tree; it is never canceled
At Google, we require that Go programmers pass a Context parameter as the first argument to every function on the call path between incoming and outgoing requests. This allows Go code developed by many different teams to interoperate well. It provides simple control over timeouts and cancellation and ensures that critical values like security credentials transit Go programs properly.
many Go APIs, especially modern ones, the first argument to functions and methods is often context.Context
Context provides a means of transmitting deadlines, caller cancellations, and other request-scoped values across API boundaries and between processes. It is often used when a library interacts — directly or transitively — with remote servers, such as databases, APIs, and the like.
Context makes it easy to propagate important cross-library and cross-API information down a calling stack. But, it must be used consistently and clearly in order to remain comprehensible, easy to debug, and effective.
When passed as the first argument in a method rather than stored in a struct type, users can take full advantage of its extensibility in order to build a powerful tree of cancellation, deadline, and metadata information through the call stack. And, best of all, its scope is clearly understood when it’s passed in as an argument, leading to clear comprehension and debuggability up and down the stack.
When designing an API with context, remember the advice: pass context.Context in as an argument; don’t store it in structs.
Golang Context is a tool that is used to share request-scoped data, cancellation signals, and timeouts or deadlines across API layers or processes in a program. It is one of the most important tools while working with concurrent programming in Go
example of request-scoped data would be the body, headers, or params of an API request
One important point to note is cancellation doesn’t automatically stop the execution, cancel just closes the Done channel which we need to use to terminate processes
In practical implementations, we usually work with derived contexts. We create a parent context and pass it across a layer, we derive a new context with it adding some additional information and passing it again to the next layer, and so on
Often combined with goroutines, it simplifies data processing, cancellation, and other operations. As an interface with only four functions, time(Deadline), signal(Done), exception(error), and data(Value), it is not complicated but super-useful in different scenarios, covering almost every aspect in common usage.
With goroutines’ lightweight feature, it is very common for a program to enable hundreds of them, one of Go’s advantages over other high-level languages, such as Java. It is also vital in the cloud era, saving resources and being more scalable
how can we terminate goroutines early when we don’t need them anymore while thousands of goroutines are still in execution?
After creating the parent context via context.Background(), we can derive more sub-contexts from it by the four With* functions provided by the context package.
- func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
- func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
- func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
- func WithValue(parent Context, key, val interface{}) Context