项目作者: mgkanani

项目描述 :
Goroutine Local Storage
高级语言: Go
项目地址: git://github.com/mgkanani/gls.git
创建时间: 2020-08-19T14:09:16Z
项目社区:https://github.com/mgkanani/gls

开源协议:MIT License

下载


Goroutine Local storage

Build Status
Coverage Status
Go Report Card
GoDoc
PkgGoDev

This Library is implementation of Thread Local Storage pattern in golang.
Go as a language hides underneath complexities associated with a goroutine from intra-communication(through channels)
to blocking/unblocking it.
Unlike Java, golang provides very minimal control over goroutines. Under these circumstances, goroutine local storage(GLS) pattern is tricky.
In most of the scenarios, gls can be avoided. GLS pattern follows implicit communication mechanism and can lead to code-complexities.
However, in some cases GLS-benefits overweight their cons.

There have been certain push from many folks to provide GLS with standard golang packages.
Later, core golang developers decided not to provide GLS with standard golang packages.

How to use

  1. package xyz
  2. import "github.com/mgkanani/gls"
  3. // to store context/data into gls-store
  4. gls.Set(ctx)
  5. // to retrieve context/data from gls-store
  6. ctx := gls.Get()
  7. // ctx is of type interface{}, need to be verified against nil and type casted
  8. // To propagate ctx/data across go-routines, pass ctx along with other values into channel.
  9. // Just before sending ctx into channel, ensure invoking 'gls.Set(nil)'.
  10. // Otherwise it can lead to race conditions for ctx.

Under the hood

This Library provides GLS with high performances. Over the period, access to gls-backed value is lock-less.
This library is built using:

  • sync.Map to store value for go-routines
  • Scheduler’s go-routines re-usability
  • Sharding to avoid performance bottleneck in Map.Store initially

After sometime, no new goroutines are created but scheduler uses existing one.
map stabilises and read-write are atomically performed without locking.

Below tests/experiments have been carried out to verify its safety and more can be added:

  • Obvious validating and coverage tests
  • Memory usage pattern over the time where goroutines(fix-numbered) continuously using GLS —> validates goroutine reuse design(i.e. Caveats-1), validates memory is not continuously increasing.
  • Long running(1 week) tests and verify memory usage //todo
Caveats:
  • The solution is leveraging golang’s goroutine reuse design hence any change around it may hamper the performance of this Library.
  • Memory requirement:
    • Best, Avg cases: 2*pointer-size for every key inserted, Worst-case: 4*pointer-size
Comparison with Read-Write Mutex:

This comparison is more between sync.Map vs sync.RWMutex backed map for gls pattern with below scenario:

  • At the end of goroutine del() is being called TestGoRtnReUsageStatsWithDel
  • At the end of goroutine del() is not called TestGoRtnReUsageStatsWithoutDel

At the start of every iteration, 100K go-routines spawned. Each go routine performs 40 Get:Set in ratio 3:1 operations. There are 50 such iterations.

System configuration:

  • 2 cores (core 2 duo)
  • ~700MB free out of 2GB before below run
  1. $ go test -v ./...
  2. ...
  3. === RUN TestGoRtnReUsageStatsWithoutDel
  4. Min:740.124215ms, Max:2.37434027s, Avg:1.103566728s
  5. time taken: 55.178477167s
  6. --- PASS: TestGoRtnReUsageStatsWithoutDel (57.87s)
  7. === RUN TestGoRtnReUsageStatsWithDel
  8. Min:737.743745ms, Max:2.134757081s, Avg:1.238451248s
  9. time taken: 1m1.923180247s
  10. --- PASS: TestGoRtnReUsageStatsWithDel (62.93s)
  11. PASS
  12. ok _/home/mahendra/gls/gls 124.513s
  13. ...
  14. === RUN TestGoRtnReUsageStatsWithoutDel
  15. Min:1.410195319s, Max:5.135148053s, Avg:3.359963156s
  16. time taken: 2m47.998293153s
  17. --- PASS: TestGoRtnReUsageStatsWithoutDel (172.19s)
  18. === RUN TestGoRtnReUsageStatsWithDel
  19. Min:1.73240396s, Max:3.50120815s, Avg:2.423918734s
  20. time taken: 2m1.196079057s
  21. --- PASS: TestGoRtnReUsageStatsWithDel (123.50s)
  22. PASS
  23. ok _/home/mahendra/gls/gls/rwmutex 302.087s

From above data, it is clear that gls is performing best and takes less than half-time compared to ReadWriteMutex based approach.

Supportability:

The library have been tested only on below architectures. However, it can work in other as well.

  • go version: 1.9+
  • arch: amd64