Skip to content

Commit 317a38e

Browse files
dgrisonnetdims
authored andcommitted
cache/memory: replace lock with sync.Map for container cache
Replace the global RWMutex-protected map with sync.Map in InMemoryCache. This provides lock-free reads for the common path (RecentStats lookups) while maintaining thread-safe writes. Add containerCacheMap typed wrapper around sync.Map to eliminate boilerplate type assertions and improve code readability. Signed-off-by: Damien Grisonnet <[email protected]>
1 parent 627928e commit 317a38e

File tree

1 file changed

+47
-37
lines changed

1 file changed

+47
-37
lines changed

cache/memory/memory.go

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,38 @@ import (
2929
// ErrDataNotFound is the error resulting if failed to find a container in memory cache.
3030
var ErrDataNotFound = errors.New("unable to find data in memory cache")
3131

32+
// containerCacheMap is a typed wrapper around sync.Map that eliminates the need
33+
// for type assertions at every call site. It stores container name strings
34+
// mapped to *containerCache values.
35+
type containerCacheMap struct {
36+
m sync.Map
37+
}
38+
39+
// Load retrieves a container cache by name. Returns nil, false if not found.
40+
func (c *containerCacheMap) Load(name string) (*containerCache, bool) {
41+
v, ok := c.m.Load(name)
42+
if !ok {
43+
return nil, false
44+
}
45+
return v.(*containerCache), true
46+
}
47+
48+
// Store saves a container cache with the given name.
49+
func (c *containerCacheMap) Store(name string, cache *containerCache) {
50+
c.m.Store(name, cache)
51+
}
52+
53+
// LoadOrStore returns the existing cache if present, otherwise stores and returns the given one.
54+
func (c *containerCacheMap) LoadOrStore(name string, cache *containerCache) (*containerCache, bool) {
55+
v, loaded := c.m.LoadOrStore(name, cache)
56+
return v.(*containerCache), loaded
57+
}
58+
59+
// Delete removes a container cache by name.
60+
func (c *containerCacheMap) Delete(name string) {
61+
c.m.Delete(name)
62+
}
63+
3264
// TODO(vmarmol): See about refactoring this class, we have an unnecessary redirection of containerCache and InMemoryCache.
3365
// containerCache is used to store per-container information
3466
type containerCache struct {
@@ -67,24 +99,18 @@ func newContainerStore(ref info.ContainerReference, maxAge time.Duration) *conta
6799
}
68100

69101
type InMemoryCache struct {
70-
lock sync.RWMutex
71-
containerCacheMap map[string]*containerCache
102+
containerCacheMap containerCacheMap
72103
maxAge time.Duration
73104
backend []storage.StorageDriver
74105
}
75106

76107
func (c *InMemoryCache) AddStats(cInfo *info.ContainerInfo, stats *info.ContainerStats) error {
77-
var cstore *containerCache
78-
var ok bool
79-
80-
func() {
81-
c.lock.Lock()
82-
defer c.lock.Unlock()
83-
if cstore, ok = c.containerCacheMap[cInfo.ContainerReference.Name]; !ok {
84-
cstore = newContainerStore(cInfo.ContainerReference, c.maxAge)
85-
c.containerCacheMap[cInfo.ContainerReference.Name] = cstore
86-
}
87-
}()
108+
name := cInfo.ContainerReference.Name
109+
cstore, ok := c.containerCacheMap.Load(name)
110+
if !ok {
111+
newStore := newContainerStore(cInfo.ContainerReference, c.maxAge)
112+
cstore, _ = c.containerCacheMap.LoadOrStore(name, newStore)
113+
}
88114

89115
for _, backend := range c.backend {
90116
// TODO(monnand): To deal with long delay write operations, we
@@ -98,45 +124,29 @@ func (c *InMemoryCache) AddStats(cInfo *info.ContainerInfo, stats *info.Containe
98124
}
99125

100126
func (c *InMemoryCache) RecentStats(name string, start, end time.Time, maxStats int) ([]*info.ContainerStats, error) {
101-
var cstore *containerCache
102-
var ok bool
103-
err := func() error {
104-
c.lock.RLock()
105-
defer c.lock.RUnlock()
106-
if cstore, ok = c.containerCacheMap[name]; !ok {
107-
return ErrDataNotFound
108-
}
109-
return nil
110-
}()
111-
if err != nil {
112-
return nil, err
127+
cstore, ok := c.containerCacheMap.Load(name)
128+
if !ok {
129+
return nil, ErrDataNotFound
113130
}
114-
115131
return cstore.RecentStats(start, end, maxStats)
116132
}
117133

118134
func (c *InMemoryCache) Close() error {
119-
c.lock.Lock()
120-
c.containerCacheMap = make(map[string]*containerCache, 32)
121-
c.lock.Unlock()
135+
c.containerCacheMap = containerCacheMap{}
122136
return nil
123137
}
124138

125139
func (c *InMemoryCache) RemoveContainer(containerName string) error {
126-
c.lock.Lock()
127-
delete(c.containerCacheMap, containerName)
128-
c.lock.Unlock()
140+
c.containerCacheMap.Delete(containerName)
129141
return nil
130142
}
131143

132144
func New(
133145
maxAge time.Duration,
134146
backend []storage.StorageDriver,
135147
) *InMemoryCache {
136-
ret := &InMemoryCache{
137-
containerCacheMap: make(map[string]*containerCache, 32),
138-
maxAge: maxAge,
139-
backend: backend,
148+
return &InMemoryCache{
149+
maxAge: maxAge,
150+
backend: backend,
140151
}
141-
return ret
142152
}

0 commit comments

Comments
 (0)