Goroutine local storage

gls

Goroutine local storage

IMPORTANT NOTE

It is my duty to point you to https://blog.golang.org/context, which is how Google solves all of the problems you'd perhaps consider using this package for at scale.

One downside to Google's approach is that all of your functions must have a new first argument, but after clearing that hurdle everything else is much better.

If you aren't interested in this warning, read on.

Huhwaht? Why?

Every so often, a thread shows up on the golang-nuts asking for some form of goroutine-local-storage, or some kind of goroutine id, or some kind of context. There are a few valid use cases for goroutine-local-storage, one of the most prominent being log line context. One poster was interested in being able to log an HTTP request context id in every log line in the same goroutine as the incoming HTTP request, without having to change every library and function call he was interested in logging.

This would be pretty useful. Provided that you could get some kind of goroutine-local-storage, you could call log.SetOutput with your own logging writer that checks goroutine-local-storage for some context information and adds that context to your log lines.

But alas, Andrew Gerrand's typically diplomatic answer to the question of goroutine-local variables was:

We wouldn't even be having this discussion if thread local storage wasn't useful. But every feature comes at a cost, and in my opinion the cost of threadlocals far outweighs their benefits. They're just not a good fit for Go.

So, yeah, that makes sense. That's a pretty good reason for why the language won't support a specific and (relatively) unuseful feature that requires some runtime changes, just for the sake of a little bit of log improvement.

But does Go require runtime changes?

How it works

Go has pretty fantastic introspective and reflective features, but one thing Go doesn't give you is any kind of access to the stack pointer, or frame pointer, or goroutine id, or anything contextual about your current stack. It gives you access to your list of callers, but only along with program counters, which are fixed at compile time.

But it does give you the stack.

So, we define 16 special functions and embed base-16 tags into the stack using the call order of those 16 functions. Then, we can read our tags back out of the stack looking at the callers list.

We then use these tags as an index into a traditional map for implementing this library.

What are people saying?

"Wow, that's horrifying."

"This is the most terrible thing I have seen in a very long time."

"Where is it getting a context from? Is this serializing all the requests? What the heck is the client being bound to? What are these tags? Why does he need callers? Oh god no. No no no."

Docs

Please see the docs at http://godoc.org/github.com/jtolds/gls

Related

If you're okay relying on the string format of the current runtime stacktrace including a unique goroutine id (not guaranteed by the spec or anything, but very unlikely to change within a Go release), you might be able to squeeze out a bit more performance by using this similar library, inspired by some code Brad Fitzpatrick wrote for debugging his HTTP/2 library: https://github.com/tylerb/gls (in contrast, jtolds/gls doesn't require any knowledge of the string format of the runtime stacktrace, which probably adds unnecessary overhead).

Owner
JT Olio
CTO@storj, he/him
JT Olio
Comments
  • Add support for GopherJS build target

    Add support for GopherJS build target

    This PR is motivated by my desire to use GoConvey to test Go code targeted for GopherJS. GoConvey depends heavily on this gls, which in turn depends heavily on the native Go runtime package, which is not well supported by GopherJS (for good reason).

    This patch uses Node.js runtime information to emulate the Go runtime functionality, when run in a GopherJS environment. This is untested in the browser, and is actually quite likely to break in non-V8 browsers, due to the fact that stack traces are formatted differently. For my needs, browser support is unimportant, so I didn't worry about this.

    The original version of gls fails in GopherJS:

    --- FAIL: TestContexts (0.01s)
            test.044076904:23193: expected value val1c for key key1, got no value
    FAIL
    

    After my changes, all tests pass in both standard go (i.e. go test) and in GopherJS (i.e. gopherjs test).

  • [Question] Looking for Context propagation mechanism for Apache SkyWalking golang sdk

    [Question] Looking for Context propagation mechanism for Apache SkyWalking golang sdk

    Hi @jtolds , glad to find these cool codes. I am the original creator and current PPMC, committer of Apache SkyWalking (incubating) project: https://github.com/apache/incubator-skywalking

    I am a rookie and new for golang ecosystem, and aware of building an auto-instrument(I prefer most) is possible but hard. So the first step is providing a manual instrument sdk based on SkyWalking model, like you did in monkit-zipkin.

    So I definitely look for the thread local propagation for matching our tracing context : https://github.com/apache/incubator-skywalking/blob/master/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/ContextManager.java#L46 I assume the gls library can do that? Am I right?

    And also, I found your spacemonkeygo/monkit library, but look like it is more likely based on a pure manual context propagation. Do I get the point right?

    I hope you can have time to help with our Apache project.

  • SetValues breaks in Go 1.12

    SetValues breaks in Go 1.12

    A simple example below. GetValue should return 0, true, but in Go 1.12, it returns 0, false.

    package main
    
    import (
    	"fmt"
    
    	"github.com/jtolds/gls"
    )
    
    func main() {
    	mgr := gls.NewContextManager()
    	mgr.SetValues(gls.Values{"node": 1}, func() {
    		fmt.Println(mgr.GetValue("node"))
    	})
    }
    
  • Not working at Go tip

    Not working at Go tip

    gls currently does not work at Go tip - it appears to have broken somewhere between 1.11.2 and golang/go@78c0e1f81d4052f8ca5a50e4e7c5bb35ddec6519.

    This means that the goconvey package doesn't work any more, since it relies on gls for context tracking.

    Test code:

    package main
    
    import (
    	"fmt"
    	"runtime"
    
    	"github.com/jtolds/gls"
    )
    
    func main() {
    	fmt.Printf("Go version: %s\n", runtime.Version())
    	manager := gls.NewContextManager()
    
    	manager.SetValues(gls.Values{
    		"foo": 1,
    	}, func() {
    		foo, ok := manager.GetValue("foo")
    		fmt.Printf("foo, ok = %+v, %t\n", foo, ok)
    	})
    }
    

    Output on recent versions:

    Go version: go1.11.1
    foo, ok = 1, true
    
    Go version: go1.11.2
    foo, ok = 1, true
    
    Go version: devel +78c0e1f Fri Nov 9 19:57:57 2018 +0000
    foo, ok = <nil>, false
    
  • latest go development version breaks gls

    latest go development version breaks gls

    see for example https://github.com/smartystreets/goconvey/issues/476 somebody said it's due to improved inlining (removing the tag frames from the call stack).

  • Cleanup stack_tags to make the trick more obvious

    Cleanup stack_tags to make the trick more obvious

    You can take this or leave it, as you like :smile:. I realize that the project overall is kind of horrifying, but I thought it could use a bit of cleanup anyway. All the tests still pass as well. The functionality of stack_tags is clearer now, IMO.

  • Usage match

    Usage match

    Hey, I'm not sure if you're still interested in this library, but I was wondering if you could answer a question for me. My use case is similar to the second one mentioned in the read me. I'd like to find a way to identify an HTTP request context for metric purposes. Rather than passing around a context I'd like to consider a goroutine as the context so I can work out timing information on a per request basis.

    Would your library work for this, or is it a bit of overkill since I'm really only interested in having an identifier for each goroutine (and possibly looking up the goroutine hierarchy to see if an ancestor is a known goroutine).

  • remove mutex lock in a goroutine lifecycle

    remove mutex lock in a goroutine lifecycle

    There is a performance loss due to mutex locks in situations where goroutines are frequently created/exited.

    1. I used lock free pool as goroutine id pool.
    2. In order to get the "Values" corresponding to the goroutine id, I made it possible to access without contention by using a pre-assigned array instead of map. It is assumed that the array can grow variably and holds write locks only when it needs to be grown.

    It was confirmed that there is a significant performance difference by modifying the benchmark test case.

    • before
    BenchmarkGetValue-10    	    2514	    551195 ns/op
    BenchmarkSetValues-10    	    6830	    166487 ns/op
    
    • after
    BenchmarkGetValue-10    	    4240	    505839 ns/op
    BenchmarkSetValues-10    	   15340	     76396 ns/op
    
  • Question: interested in joining efforts with an alternative implementation?

    Question: interested in joining efforts with an alternative implementation?

    For my Go interpreter gomacro, I had to use goroutine-local storage so I implemented https://github.com/cosmos72/gls - it uses a single line of assembler to retrieve a goroutine id.

    Would you be interested in comparing the two implementations and perhaps add to each one the best features of the other?

  • Enable go modules

    Enable go modules

    https://github.com/golang/go/wiki/Modules#gomod

    This PR is just a result of the following commands (in clean Go 1.11.5 environment):

    go mod init
    go get ./...
    go mod tidy
    

    The aim is to prevent gls from being marked as "incompatible" in go-mod-aware modules which depend on gls. This happens just because gls is >1.0.0 (currently v4.20) but isn't go-mod-aware. See https://forum.golangbridge.org/t/go-get-marking-go-mod-incompatible/10686 for more.

Related tags
A Go implementation of an in-memory bloom filter, with support for boltdb and badgerdb as optional data persistent storage.

Sprout A bloom filter is a probabilistic data structure that is used to determine if an element is present in a set. Bloom filters are fast and space

Jul 4, 2022
Goroutine local storage

gls Goroutine local storage IMPORTANT NOTE It is my duty to point you to https://blog.golang.org/context, which is how Google solves all of the proble

Dec 22, 2022
Provides some convenient API, includes Goid(), AllGoid(), and LocalStorage, which is a goroutine's local storage, just like ThreadLocal in other languages.

routine 中文版 routine encapsulates and provides some easy-to-use, high-performance goroutine context access interfaces, which can help you access corout

Dec 30, 2022
🐜🐜🐜 ants is a high-performance and low-cost goroutine pool in Go, inspired by fasthttp./ ants 是一个高性能且低损耗的 goroutine 池。
🐜🐜🐜 ants is a high-performance and low-cost goroutine pool in Go, inspired by fasthttp./ ants 是一个高性能且低损耗的 goroutine 池。

A goroutine pool for Go English | ???? 中文 ?? Introduction Library ants implements a goroutine pool with fixed capacity, managing and recycling a massi

Jan 2, 2023
Local Storage is one of HwameiStor components. It will provision the local LVM volume.
Local Storage is one of HwameiStor components. It will provision the local LVM volume.

Local Storage Module English | Simplified_Chinese Introduction Local Storage is one of modules of HwameiStor which is a cloud native local storage sys

Aug 6, 2022
K8s local storage sync for stateful set's using microk8s-hostpath storage classe

Local Storage Sync for microk8s-hostpath The goal is to be able to sync stateful sets between the different nodes of a cluster to allow the data to be

Nov 1, 2022
Openshift's hpessa-exporter allows users to export SMART information of local storage devices as Prometheus metrics, by using HPE Smart Storage Administrator tool

hpessa-exporter Overview Openshift's hpessa-exporter allows users to export SMART information of local storage devices as Prometheus metrics, by using

Jan 17, 2022
The Container Storage Interface (CSI) Driver for Fortress Block Storage This driver allows you to use Fortress Block Storage with your container orchestrator

fortress-csi The Container Storage Interface (CSI) Driver for Fortress Block Storage This driver allows you to use Fortress Block Storage with your co

Jan 23, 2022
A cross goroutine storage tool with very simple implementation and function.

Simple-goroutine-local is a cross goroutine storage tool with very simple implementation and function (the concept is similar to Java ThreadLocal). Ge

Jan 13, 2022
Open-Local is a local disk management system composed of multiple components.
Open-Local is a local disk management system composed of multiple components.

Open-Local is a local disk management system composed of multiple components. With Open-Local, using local storage in Kubernetes will be as simple as centralized storage.

Dec 30, 2022
topolvm operator provide kubernetes local storage which is light weight and high performance

Topolvm-Operator Topolvm-Operator is an open source cloud-native local storage orchestrator for Kubernetes, which bases on topolvm. Supported environm

Nov 24, 2022
tstorage is a lightweight local on-disk storage engine for time-series data
tstorage is a lightweight local on-disk storage engine for time-series data

tstorage is a lightweight local on-disk storage engine for time-series data with a straightforward API. Especially ingestion is massively opt

Jan 1, 2023
cloud-native local storage management system
cloud-native local storage management system

Open-Local是由多个组件构成的本地磁盘管理系统,目标是解决当前 Kubernetes 本地存储能力缺失问题。通过Open-Local,使用本地存储会像集中式存储一样简单。

Dec 30, 2022
An high performance and ops-free local storage solution for Kubernetes.
An high performance and ops-free local storage solution for Kubernetes.

Carina carina 是一个CSI插件,在Kubernetes集群中提供本地存储持久卷 项目状态:开发测试中 CSI Version: 1.3.0 Carina architecture 支持的环境 Kubernetes:1.20 1.19 1.18 Node OS:Linux Filesys

May 18, 2022
storage interface for local disk or AWS S3 (or Minio) platform

storage interface for local disk or AWS S3 (or Minio) platform

Apr 19, 2022
Carina: an high performance and ops-free local storage for kubernetes
Carina: an high performance and ops-free local storage for kubernetes

Carina English | 中文 Background Storage systems are complex! There are more and more kubernetes native storage systems nowadays and stateful applicatio

Dec 30, 2022
Dynamically provisioning persistent local storage with Kubernetes

Local Path Provisioner Overview Local Path Provisioner provides a way for the Kubernetes users to utilize the local storage in each node. Based on the

Jan 4, 2023
SMART information of local storage devices as Prometheus metrics

hpessa-exporter Overview Openshift's hpessa-exporter allows users to export SMART information of local storage devices as Prometheus metrics, by using

Feb 10, 2022