VictoriaMetrics/lib/logstorage/cache.go
Aliaksandr Valialkin 62e4baf556
lib/logstorage: use simpler in-memory cache instead of workingsetcache for caching recently ingested _stream values and recently queried set of streams
These caches aren't expected to grow big, so it is OK to use the most simplest cache based on sync.Map.
The benefit of this cache compared to workingsetcache is better scalability on systems with many CPU cores,
since it doesn't use mutexes at fast path.
An additional benefit is lower memory usage on average, since the size of in-memory cache equals
working set for the last 3 minutes.

The downside is that there is no upper bound for the cache size, so it may grow big during workload spikes.
But this is very unlikely for typical workloads.

(cherry picked from commit 0f24078146)
2024-10-18 11:42:16 +02:00

83 lines
1.3 KiB
Go

package logstorage
import (
"strings"
"sync"
"sync/atomic"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/timeutil"
)
type cache struct {
curr atomic.Pointer[sync.Map]
prev atomic.Pointer[sync.Map]
stopCh chan struct{}
wg sync.WaitGroup
}
func newCache() *cache {
var c cache
c.curr.Store(&sync.Map{})
c.prev.Store(&sync.Map{})
c.stopCh = make(chan struct{})
c.wg.Add(1)
go func() {
defer c.wg.Done()
c.runCleaner()
}()
return &c
}
func (c *cache) MustStop() {
close(c.stopCh)
c.wg.Wait()
}
func (c *cache) runCleaner() {
d := timeutil.AddJitterToDuration(3 * time.Minute)
t := time.NewTicker(d)
defer t.Stop()
for {
select {
case <-t.C:
c.clean()
case <-c.stopCh:
return
}
}
}
func (c *cache) clean() {
curr := c.curr.Load()
c.prev.Store(curr)
c.curr.Store(&sync.Map{})
}
func (c *cache) Get(k []byte) (any, bool) {
kStr := bytesutil.ToUnsafeString(k)
curr := c.curr.Load()
v, ok := curr.Load(kStr)
if ok {
return v, true
}
prev := c.prev.Load()
v, ok = prev.Load(kStr)
if ok {
kStr = strings.Clone(kStr)
curr.Store(kStr, v)
return v, true
}
return nil, false
}
func (c *cache) Set(k []byte, v any) {
kStr := string(k)
curr := c.curr.Load()
curr.Store(kStr, v)
}