fix(shell): lock template cache correctly

This commit is contained in:
Jan De Dobbeleer 2022-12-28 10:12:53 +01:00 committed by Jan De Dobbeleer
parent 3cdb3bfc1d
commit dee040c719
3 changed files with 37 additions and 33 deletions

View file

@ -25,13 +25,13 @@ func (c *cacheObject) expired() bool {
} }
type fileCache struct { type fileCache struct {
cache *concurrentMap cache *ConcurrentMap
cachePath string cachePath string
dirty bool dirty bool
} }
func (fc *fileCache) Init(cachePath string) { func (fc *fileCache) Init(cachePath string) {
fc.cache = newConcurrentMap() fc.cache = NewConcurrentMap()
fc.cachePath = cachePath fc.cachePath = cachePath
cacheFilePath := filepath.Join(fc.cachePath, CacheFile) cacheFilePath := filepath.Join(fc.cachePath, CacheFile)
content, err := os.ReadFile(cacheFilePath) content, err := os.ReadFile(cacheFilePath)
@ -48,7 +48,7 @@ func (fc *fileCache) Init(cachePath string) {
if co.expired() { if co.expired() {
continue continue
} }
fc.cache.set(key, co) fc.cache.Set(key, co)
} }
} }
@ -56,7 +56,7 @@ func (fc *fileCache) Close() {
if !fc.dirty { if !fc.dirty {
return return
} }
cache := fc.cache.list() cache := fc.cache.List()
if dump, err := json.MarshalIndent(cache, "", " "); err == nil { if dump, err := json.MarshalIndent(cache, "", " "); err == nil {
cacheFilePath := filepath.Join(fc.cachePath, CacheFile) cacheFilePath := filepath.Join(fc.cachePath, CacheFile)
_ = os.WriteFile(cacheFilePath, dump, 0644) _ = os.WriteFile(cacheFilePath, dump, 0644)
@ -66,7 +66,7 @@ func (fc *fileCache) Close() {
// returns the value for the given key as long as // returns the value for the given key as long as
// the TTL (minutes) is not expired // the TTL (minutes) is not expired
func (fc *fileCache) Get(key string) (string, bool) { func (fc *fileCache) Get(key string) (string, bool) {
val, found := fc.cache.get(key) val, found := fc.cache.Get(key)
if !found { if !found {
return "", false return "", false
} }
@ -78,7 +78,7 @@ func (fc *fileCache) Get(key string) (string, bool) {
// sets the value for the given key with a TTL (minutes) // sets the value for the given key with a TTL (minutes)
func (fc *fileCache) Set(key, value string, ttl int) { func (fc *fileCache) Set(key, value string, ttl int) {
fc.cache.set(key, &cacheObject{ fc.cache.Set(key, &cacheObject{
Value: value, Value: value,
Timestamp: time.Now().Unix(), Timestamp: time.Now().Unix(),
TTL: ttl, TTL: ttl,

View file

@ -1,30 +1,33 @@
package platform package platform
type concurrentMap struct { import "sync"
type ConcurrentMap struct {
values map[string]interface{} values map[string]interface{}
sync.RWMutex
} }
func newConcurrentMap() *concurrentMap { func NewConcurrentMap() *ConcurrentMap {
return &concurrentMap{ return &ConcurrentMap{
values: make(map[string]interface{}), values: make(map[string]interface{}),
} }
} }
func (c *concurrentMap) set(key string, value interface{}) { func (c *ConcurrentMap) Set(key string, value interface{}) {
lock.Lock() c.Lock()
defer lock.Unlock() defer c.Unlock()
c.values[key] = value c.values[key] = value
} }
func (c *concurrentMap) get(key string) (interface{}, bool) { func (c *ConcurrentMap) Get(key string) (interface{}, bool) {
lock.RLock() c.RLock()
defer lock.RUnlock() defer c.RUnlock()
if val, ok := c.values[key]; ok { if val, ok := c.values[key]; ok {
return val, true return val, true
} }
return "", false return "", false
} }
func (c *concurrentMap) list() map[string]interface{} { func (c *ConcurrentMap) List() map[string]interface{} {
return c.values return c.values
} }

View file

@ -42,7 +42,6 @@ func getPID() string {
} }
var ( var (
lock = sync.RWMutex{}
TEMPLATECACHE = fmt.Sprintf("template_cache_%s", getPID()) TEMPLATECACHE = fmt.Sprintf("template_cache_%s", getPID())
TOGGLECACHE = fmt.Sprintf("toggle_cache_%s", getPID()) TOGGLECACHE = fmt.Sprintf("toggle_cache_%s", getPID())
) )
@ -131,8 +130,8 @@ type Connection struct {
type SegmentsCache map[string]interface{} type SegmentsCache map[string]interface{}
func (c *SegmentsCache) Contains(key string) bool { func (s *SegmentsCache) Contains(key string) bool {
_, ok := (*c)[key] _, ok := (*s)[key]
return ok return ok
} }
@ -149,15 +148,14 @@ type TemplateCache struct {
OS string OS string
WSL bool WSL bool
Segments SegmentsCache Segments SegmentsCache
sync.RWMutex
} }
func (t *TemplateCache) AddSegmentData(key string, value interface{}) { func (t *TemplateCache) AddSegmentData(key string, value interface{}) {
lock.Lock() t.Lock()
defer lock.Unlock()
if t.Segments == nil {
t.Segments = make(map[string]interface{})
}
t.Segments[key] = value t.Segments[key] = value
t.Unlock()
} }
type Environment interface { type Environment interface {
@ -212,15 +210,15 @@ type Environment interface {
} }
type commandCache struct { type commandCache struct {
commands *concurrentMap commands *ConcurrentMap
} }
func (c *commandCache) set(command, path string) { func (c *commandCache) set(command, path string) {
c.commands.set(command, path) c.commands.Set(command, path)
} }
func (c *commandCache) get(command string) (string, bool) { func (c *commandCache) get(command string) (string, bool) {
cacheCommand, found := c.commands.get(command) cacheCommand, found := c.commands.Get(command)
if !found { if !found {
return "", false return "", false
} }
@ -237,6 +235,8 @@ type Shell struct {
fileCache *fileCache fileCache *fileCache
tmplCache *TemplateCache tmplCache *TemplateCache
networks []*Connection networks []*Connection
sync.RWMutex
} }
func (env *Shell) Init() { func (env *Shell) Init() {
@ -251,7 +251,7 @@ func (env *Shell) Init() {
env.fileCache.Init(env.CachePath()) env.fileCache.Init(env.CachePath())
env.resolveConfigPath() env.resolveConfigPath()
env.cmdCache = &commandCache{ env.cmdCache = &commandCache{
commands: newConcurrentMap(), commands: NewConcurrentMap(),
} }
} }
@ -331,9 +331,7 @@ func (env *Shell) Getenv(key string) string {
func (env *Shell) Pwd() string { func (env *Shell) Pwd() string {
defer env.Trace(time.Now(), "Pwd") defer env.Trace(time.Now(), "Pwd")
lock.Lock()
defer func() { defer func() {
lock.Unlock()
env.Debug("Pwd", env.cwd) env.Debug("Pwd", env.cwd)
}() }()
if env.cwd != "" { if env.cwd != "" {
@ -708,7 +706,11 @@ func (env *Shell) Logs() string {
} }
func (env *Shell) TemplateCache() *TemplateCache { func (env *Shell) TemplateCache() *TemplateCache {
defer env.Trace(time.Now(), "TemplateCache") env.Lock()
defer func() {
env.Trace(time.Now(), "TemplateCache")
env.Unlock()
}()
if env.tmplCache != nil { if env.tmplCache != nil {
return env.tmplCache return env.tmplCache
} }
@ -718,6 +720,7 @@ func (env *Shell) TemplateCache() *TemplateCache {
ShellVersion: env.CmdFlags.ShellVersion, ShellVersion: env.CmdFlags.ShellVersion,
Code: env.ErrorCode(), Code: env.ErrorCode(),
WSL: env.IsWsl(), WSL: env.IsWsl(),
Segments: make(map[string]interface{}),
} }
tmplCache.Env = make(map[string]string) tmplCache.Env = make(map[string]string)
const separator = "=" const separator = "="
@ -751,8 +754,6 @@ func (env *Shell) TemplateCache() *TemplateCache {
} }
func (env *Shell) DirMatchesOneOf(dir string, regexes []string) (match bool) { func (env *Shell) DirMatchesOneOf(dir string, regexes []string) (match bool) {
lock.Lock()
defer lock.Unlock()
// sometimes the function panics inside golang, we want to silence that error // sometimes the function panics inside golang, we want to silence that error
// and assume that there's no match. Not perfect, but better than crashing // and assume that there's no match. Not perfect, but better than crashing
// for the time being until we figure out what the actual root cause is // for the time being until we figure out what the actual root cause is