Similar to Thread Local Storage, but for Go routines.
In the request handler, it will perform complicated work invoking
many functions.
Sometimes, it's difficult and even impossible (through third-party layer)
to pass *http.Request
all the way down to every function called.
But we want most of the functions be able to access the HTTP request,
or at least be aware of some information in the HTTP request (e.g. RequestId).
With gls
, we don't need to pass request to every function which is still
able to get the request using gls.Get()
.
type Context struct {
...
}
func myWork() {
context := gls.Get().(*Context)
...
}
func handler(req *Request) {
context := contextFromRequest(req)
gls.With(context, myWork)
}
gls.Get()
requires gls.With
called, otherwise it will panic
.
So it's guaranteed the returned value is present.
To prevent panic
, use gls.GetSafe()
which returns nil
if gls.With
is never called.
The context doesn't go across go routines.
If the request must be handled in separate go routines, the context should be forwarded.
A helper gls.Go
is provided to spawn a go routine with current context forwarded:
func workerFn() {
// this is in a separate go routine
context := gls.Get().(*Context)
}
func myHandler() {
gls.Go(workerFn)
}
This above code is equivalent to:
func workerFn() {
context := gls.Get().(*Context)
}
func myHandler() {
context := gls.Get().(*Context)
go gls.With(context, workerFn)
}
Because Go routine runs on arbitrary OS thread, the functions in the same Go routine may run on different OS threads from time to time, the Thread Local Storage doesn't solve the problem. The implementation uses stack trace of current Go routine to locate a special function call with magic function name (containing a UUID). By parsing the arguments (pointer values) passed to the function, we can find the context associated with current Go routine.
The implementation is a little different on 32-bit/64-bit architectures, because the size of uint passed in the function is different.
runtime.Stack
returns maximum _TracebackMaxFrames = 100
(see src/runtime/runtime2.go
and src/runtime/traceback.go
) frames,
gls.Get
will fail to find the context if the stack is too deep.
Keep this in mind when using this library.
MIT