mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
all: use workingsetcache instead of fastcache
This should reduce the amount of RAM required for processing time series with non-zero churn rate. The previous cache behavior can be restored with `-cache.oldBehavior` command-line flag.
This commit is contained in:
parent
99c37c2c96
commit
09fc6e22e5
6 changed files with 278 additions and 62 deletions
|
@ -4,7 +4,6 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
@ -12,6 +11,7 @@ import (
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/workingsetcache"
|
||||||
"github.com/VictoriaMetrics/fastcache"
|
"github.com/VictoriaMetrics/fastcache"
|
||||||
"github.com/VictoriaMetrics/metrics"
|
"github.com/VictoriaMetrics/metrics"
|
||||||
)
|
)
|
||||||
|
@ -19,7 +19,7 @@ import (
|
||||||
var disableCache = flag.Bool("search.disableCache", false, "Whether to disable response caching. This may be useful during data backfilling")
|
var disableCache = flag.Bool("search.disableCache", false, "Whether to disable response caching. This may be useful during data backfilling")
|
||||||
|
|
||||||
var rollupResultCacheV = &rollupResultCache{
|
var rollupResultCacheV = &rollupResultCache{
|
||||||
fastcache.New(1024 * 1024), // This is a cache for testing.
|
c: workingsetcache.New(1024*1024, time.Hour), // This is a cache for testing.
|
||||||
}
|
}
|
||||||
var rollupResultCachePath string
|
var rollupResultCachePath string
|
||||||
|
|
||||||
|
@ -43,15 +43,17 @@ var (
|
||||||
func InitRollupResultCache(cachePath string) {
|
func InitRollupResultCache(cachePath string) {
|
||||||
rollupResultCachePath = cachePath
|
rollupResultCachePath = cachePath
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
var c *fastcache.Cache
|
cacheSize := getRollupResultCacheSize()
|
||||||
|
var c *workingsetcache.Cache
|
||||||
if len(rollupResultCachePath) > 0 {
|
if len(rollupResultCachePath) > 0 {
|
||||||
logger.Infof("loading rollupResult cache from %q...", rollupResultCachePath)
|
logger.Infof("loading rollupResult cache from %q...", rollupResultCachePath)
|
||||||
c = fastcache.LoadFromFileOrNew(rollupResultCachePath, getRollupResultCacheSize())
|
c = workingsetcache.Load(rollupResultCachePath, cacheSize, time.Hour)
|
||||||
} else {
|
} else {
|
||||||
c = fastcache.New(getRollupResultCacheSize())
|
c = workingsetcache.New(cacheSize, time.Hour)
|
||||||
}
|
}
|
||||||
if *disableCache {
|
if *disableCache {
|
||||||
c.Reset()
|
c.Stop()
|
||||||
|
c = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := &fastcache.Stats{}
|
stats := &fastcache.Stats{}
|
||||||
|
@ -96,25 +98,28 @@ func InitRollupResultCache(cachePath string) {
|
||||||
// StopRollupResultCache closes the rollupResult cache.
|
// StopRollupResultCache closes the rollupResult cache.
|
||||||
func StopRollupResultCache() {
|
func StopRollupResultCache() {
|
||||||
if len(rollupResultCachePath) == 0 {
|
if len(rollupResultCachePath) == 0 {
|
||||||
rollupResultCacheV.c.Reset()
|
if !*disableCache {
|
||||||
|
rollupResultCacheV.c.Stop()
|
||||||
|
rollupResultCacheV.c = nil
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gomaxprocs := runtime.GOMAXPROCS(-1)
|
|
||||||
logger.Infof("saving rollupResult cache to %q...", rollupResultCachePath)
|
logger.Infof("saving rollupResult cache to %q...", rollupResultCachePath)
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
if err := rollupResultCacheV.c.SaveToFileConcurrent(rollupResultCachePath, gomaxprocs); err != nil {
|
if err := rollupResultCacheV.c.Save(rollupResultCachePath); err != nil {
|
||||||
logger.Errorf("cannot close rollupResult cache at %q: %s", rollupResultCachePath, err)
|
logger.Errorf("cannot close rollupResult cache at %q: %s", rollupResultCachePath, err)
|
||||||
} else {
|
return
|
||||||
var fcs fastcache.Stats
|
|
||||||
rollupResultCacheV.c.UpdateStats(&fcs)
|
|
||||||
rollupResultCacheV.c.Reset()
|
|
||||||
logger.Infof("saved rollupResult cache to %q in %s; entriesCount: %d, sizeBytes: %d",
|
|
||||||
rollupResultCachePath, time.Since(startTime), fcs.EntriesCount, fcs.BytesSize)
|
|
||||||
}
|
}
|
||||||
|
var fcs fastcache.Stats
|
||||||
|
rollupResultCacheV.c.UpdateStats(&fcs)
|
||||||
|
rollupResultCacheV.c.Stop()
|
||||||
|
rollupResultCacheV.c = nil
|
||||||
|
logger.Infof("saved rollupResult cache to %q in %s; entriesCount: %d, sizeBytes: %d",
|
||||||
|
rollupResultCachePath, time.Since(startTime), fcs.EntriesCount, fcs.BytesSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
type rollupResultCache struct {
|
type rollupResultCache struct {
|
||||||
c *fastcache.Cache
|
c *workingsetcache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
var rollupResultCacheResets = metrics.NewCounter(`vm_cache_resets_total{type="promql/rollupResult"}`)
|
var rollupResultCacheResets = metrics.NewCounter(`vm_cache_resets_total{type="promql/rollupResult"}`)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/mergeset"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/mergeset"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/workingsetcache"
|
||||||
"github.com/VictoriaMetrics/fastcache"
|
"github.com/VictoriaMetrics/fastcache"
|
||||||
xxhash "github.com/cespare/xxhash/v2"
|
xxhash "github.com/cespare/xxhash/v2"
|
||||||
)
|
)
|
||||||
|
@ -52,17 +53,17 @@ type indexDB struct {
|
||||||
extDBLock sync.Mutex
|
extDBLock sync.Mutex
|
||||||
|
|
||||||
// Cache for fast TagFilters -> TSIDs lookup.
|
// Cache for fast TagFilters -> TSIDs lookup.
|
||||||
tagCache *fastcache.Cache
|
tagCache *workingsetcache.Cache
|
||||||
|
|
||||||
// Cache for fast MetricID -> TSID lookup.
|
// Cache for fast MetricID -> TSID lookup.
|
||||||
metricIDCache *fastcache.Cache
|
metricIDCache *workingsetcache.Cache
|
||||||
|
|
||||||
// Cache for fast MetricID -> MetricName lookup.
|
// Cache for fast MetricID -> MetricName lookup.
|
||||||
metricNameCache *fastcache.Cache
|
metricNameCache *workingsetcache.Cache
|
||||||
|
|
||||||
// Cache holding useless TagFilters entries, which have no tag filters
|
// Cache holding useless TagFilters entries, which have no tag filters
|
||||||
// matching low number of metrics.
|
// matching low number of metrics.
|
||||||
uselessTagFiltersCache *fastcache.Cache
|
uselessTagFiltersCache *workingsetcache.Cache
|
||||||
|
|
||||||
indexSearchPool sync.Pool
|
indexSearchPool sync.Pool
|
||||||
|
|
||||||
|
@ -101,7 +102,7 @@ type indexDB struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// openIndexDB opens index db from the given path with the given caches.
|
// openIndexDB opens index db from the given path with the given caches.
|
||||||
func openIndexDB(path string, metricIDCache, metricNameCache *fastcache.Cache, currHourMetricIDs, prevHourMetricIDs *atomic.Value) (*indexDB, error) {
|
func openIndexDB(path string, metricIDCache, metricNameCache *workingsetcache.Cache, currHourMetricIDs, prevHourMetricIDs *atomic.Value) (*indexDB, error) {
|
||||||
if metricIDCache == nil {
|
if metricIDCache == nil {
|
||||||
logger.Panicf("BUG: metricIDCache must be non-nil")
|
logger.Panicf("BUG: metricIDCache must be non-nil")
|
||||||
}
|
}
|
||||||
|
@ -130,10 +131,10 @@ func openIndexDB(path string, metricIDCache, metricNameCache *fastcache.Cache, c
|
||||||
tb: tb,
|
tb: tb,
|
||||||
name: name,
|
name: name,
|
||||||
|
|
||||||
tagCache: fastcache.New(mem / 32),
|
tagCache: workingsetcache.New(mem/32, time.Hour),
|
||||||
metricIDCache: metricIDCache,
|
metricIDCache: metricIDCache,
|
||||||
metricNameCache: metricNameCache,
|
metricNameCache: metricNameCache,
|
||||||
uselessTagFiltersCache: fastcache.New(mem / 128),
|
uselessTagFiltersCache: workingsetcache.New(mem/128, time.Hour),
|
||||||
|
|
||||||
currHourMetricIDs: currHourMetricIDs,
|
currHourMetricIDs: currHourMetricIDs,
|
||||||
prevHourMetricIDs: prevHourMetricIDs,
|
prevHourMetricIDs: prevHourMetricIDs,
|
||||||
|
@ -273,8 +274,8 @@ func (db *indexDB) decRef() {
|
||||||
db.SetExtDB(nil)
|
db.SetExtDB(nil)
|
||||||
|
|
||||||
// Free space occupied by caches owned by db.
|
// Free space occupied by caches owned by db.
|
||||||
db.tagCache.Reset()
|
db.tagCache.Stop()
|
||||||
db.uselessTagFiltersCache.Reset()
|
db.uselessTagFiltersCache.Stop()
|
||||||
|
|
||||||
db.tagCache = nil
|
db.tagCache = nil
|
||||||
db.metricIDCache = nil
|
db.metricIDCache = nil
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||||
"github.com/VictoriaMetrics/fastcache"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/workingsetcache"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMarshalUnmarshalTSIDs(t *testing.T) {
|
func TestMarshalUnmarshalTSIDs(t *testing.T) {
|
||||||
|
@ -57,10 +57,10 @@ func TestMarshalUnmarshalTSIDs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIndexDBOpenClose(t *testing.T) {
|
func TestIndexDBOpenClose(t *testing.T) {
|
||||||
metricIDCache := fastcache.New(1234)
|
metricIDCache := workingsetcache.New(1234, time.Hour)
|
||||||
metricNameCache := fastcache.New(1234)
|
metricNameCache := workingsetcache.New(1234, time.Hour)
|
||||||
defer metricIDCache.Reset()
|
defer metricIDCache.Stop()
|
||||||
defer metricNameCache.Reset()
|
defer metricNameCache.Stop()
|
||||||
|
|
||||||
var hmCurr atomic.Value
|
var hmCurr atomic.Value
|
||||||
hmCurr.Store(&hourMetricIDs{})
|
hmCurr.Store(&hourMetricIDs{})
|
||||||
|
@ -85,10 +85,10 @@ func TestIndexDB(t *testing.T) {
|
||||||
const metricGroups = 10
|
const metricGroups = 10
|
||||||
|
|
||||||
t.Run("serial", func(t *testing.T) {
|
t.Run("serial", func(t *testing.T) {
|
||||||
metricIDCache := fastcache.New(1234)
|
metricIDCache := workingsetcache.New(1234, time.Hour)
|
||||||
metricNameCache := fastcache.New(1234)
|
metricNameCache := workingsetcache.New(1234, time.Hour)
|
||||||
defer metricIDCache.Reset()
|
defer metricIDCache.Stop()
|
||||||
defer metricNameCache.Reset()
|
defer metricNameCache.Stop()
|
||||||
|
|
||||||
var hmCurr atomic.Value
|
var hmCurr atomic.Value
|
||||||
hmCurr.Store(&hourMetricIDs{})
|
hmCurr.Store(&hourMetricIDs{})
|
||||||
|
@ -142,10 +142,10 @@ func TestIndexDB(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("concurrent", func(t *testing.T) {
|
t.Run("concurrent", func(t *testing.T) {
|
||||||
metricIDCache := fastcache.New(1234)
|
metricIDCache := workingsetcache.New(1234, time.Hour)
|
||||||
metricNameCache := fastcache.New(1234)
|
metricNameCache := workingsetcache.New(1234, time.Hour)
|
||||||
defer metricIDCache.Reset()
|
defer metricIDCache.Stop()
|
||||||
defer metricNameCache.Reset()
|
defer metricNameCache.Stop()
|
||||||
|
|
||||||
var hmCurr atomic.Value
|
var hmCurr atomic.Value
|
||||||
hmCurr.Store(&hourMetricIDs{})
|
hmCurr.Store(&hourMetricIDs{})
|
||||||
|
|
|
@ -6,17 +6,18 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/fastcache"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/workingsetcache"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkIndexDBAddTSIDs(b *testing.B) {
|
func BenchmarkIndexDBAddTSIDs(b *testing.B) {
|
||||||
const recordsPerLoop = 1e3
|
const recordsPerLoop = 1e3
|
||||||
|
|
||||||
metricIDCache := fastcache.New(1234)
|
metricIDCache := workingsetcache.New(1234, time.Hour)
|
||||||
metricNameCache := fastcache.New(1234)
|
metricNameCache := workingsetcache.New(1234, time.Hour)
|
||||||
defer metricIDCache.Reset()
|
defer metricIDCache.Stop()
|
||||||
defer metricNameCache.Reset()
|
defer metricNameCache.Stop()
|
||||||
|
|
||||||
var hmCurr atomic.Value
|
var hmCurr atomic.Value
|
||||||
hmCurr.Store(&hourMetricIDs{})
|
hmCurr.Store(&hourMetricIDs{})
|
||||||
|
@ -79,10 +80,10 @@ func benchmarkIndexDBAddTSIDs(db *indexDB, tsid *TSID, mn *MetricName, startOffs
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkIndexDBGetTSIDs(b *testing.B) {
|
func BenchmarkIndexDBGetTSIDs(b *testing.B) {
|
||||||
metricIDCache := fastcache.New(1234)
|
metricIDCache := workingsetcache.New(1234, time.Hour)
|
||||||
metricNameCache := fastcache.New(1234)
|
metricNameCache := workingsetcache.New(1234, time.Hour)
|
||||||
defer metricIDCache.Reset()
|
defer metricIDCache.Stop()
|
||||||
defer metricNameCache.Reset()
|
defer metricNameCache.Stop()
|
||||||
|
|
||||||
var hmCurr atomic.Value
|
var hmCurr atomic.Value
|
||||||
hmCurr.Store(&hourMetricIDs{})
|
hmCurr.Store(&hourMetricIDs{})
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/timerpool"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/timerpool"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/workingsetcache"
|
||||||
"github.com/VictoriaMetrics/fastcache"
|
"github.com/VictoriaMetrics/fastcache"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,16 +40,16 @@ type Storage struct {
|
||||||
tb *table
|
tb *table
|
||||||
|
|
||||||
// tsidCache is MetricName -> TSID cache.
|
// tsidCache is MetricName -> TSID cache.
|
||||||
tsidCache *fastcache.Cache
|
tsidCache *workingsetcache.Cache
|
||||||
|
|
||||||
// metricIDCache is MetricID -> TSID cache.
|
// metricIDCache is MetricID -> TSID cache.
|
||||||
metricIDCache *fastcache.Cache
|
metricIDCache *workingsetcache.Cache
|
||||||
|
|
||||||
// metricNameCache is MetricID -> MetricName cache.
|
// metricNameCache is MetricID -> MetricName cache.
|
||||||
metricNameCache *fastcache.Cache
|
metricNameCache *workingsetcache.Cache
|
||||||
|
|
||||||
// dateMetricIDCache is (Date, MetricID) cache.
|
// dateMetricIDCache is (Date, MetricID) cache.
|
||||||
dateMetricIDCache *fastcache.Cache
|
dateMetricIDCache *workingsetcache.Cache
|
||||||
|
|
||||||
// Fast cache for MetricID values occured during the current hour.
|
// Fast cache for MetricID values occured during the current hour.
|
||||||
currHourMetricIDs atomic.Value
|
currHourMetricIDs atomic.Value
|
||||||
|
@ -460,10 +461,10 @@ func (s *Storage) MustClose() {
|
||||||
s.idb().MustClose()
|
s.idb().MustClose()
|
||||||
|
|
||||||
// Save caches.
|
// Save caches.
|
||||||
s.mustSaveCache(s.tsidCache, "MetricName->TSID", "metricName_tsid")
|
s.mustSaveAndStopCache(s.tsidCache, "MetricName->TSID", "metricName_tsid")
|
||||||
s.mustSaveCache(s.metricIDCache, "MetricID->TSID", "metricID_tsid")
|
s.mustSaveAndStopCache(s.metricIDCache, "MetricID->TSID", "metricID_tsid")
|
||||||
s.mustSaveCache(s.metricNameCache, "MetricID->MetricName", "metricID_metricName")
|
s.mustSaveAndStopCache(s.metricNameCache, "MetricID->MetricName", "metricID_metricName")
|
||||||
s.mustSaveCache(s.dateMetricIDCache, "Date->MetricID", "date_metricID")
|
s.mustSaveAndStopCache(s.dateMetricIDCache, "Date->MetricID", "date_metricID")
|
||||||
|
|
||||||
hmCurr := s.currHourMetricIDs.Load().(*hourMetricIDs)
|
hmCurr := s.currHourMetricIDs.Load().(*hourMetricIDs)
|
||||||
s.mustSaveHourMetricIDs(hmCurr, "curr_hour_metric_ids")
|
s.mustSaveHourMetricIDs(hmCurr, "curr_hour_metric_ids")
|
||||||
|
@ -542,11 +543,11 @@ func (s *Storage) mustSaveHourMetricIDs(hm *hourMetricIDs, name string) {
|
||||||
logger.Infof("saved %s to %q in %s; entriesCount: %d; sizeBytes: %d", name, path, time.Since(startTime), len(hm.m), len(dst))
|
logger.Infof("saved %s to %q in %s; entriesCount: %d; sizeBytes: %d", name, path, time.Since(startTime), len(hm.m), len(dst))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) mustLoadCache(info, name string, sizeBytes int) *fastcache.Cache {
|
func (s *Storage) mustLoadCache(info, name string, sizeBytes int) *workingsetcache.Cache {
|
||||||
path := s.cachePath + "/" + name
|
path := s.cachePath + "/" + name
|
||||||
logger.Infof("loading %s cache from %q...", info, path)
|
logger.Infof("loading %s cache from %q...", info, path)
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
c := fastcache.LoadFromFileOrNew(path, sizeBytes)
|
c := workingsetcache.Load(path, sizeBytes, time.Hour)
|
||||||
var cs fastcache.Stats
|
var cs fastcache.Stats
|
||||||
c.UpdateStats(&cs)
|
c.UpdateStats(&cs)
|
||||||
logger.Infof("loaded %s cache from %q in %s; entriesCount: %d; sizeBytes: %d",
|
logger.Infof("loaded %s cache from %q in %s; entriesCount: %d; sizeBytes: %d",
|
||||||
|
@ -554,17 +555,16 @@ func (s *Storage) mustLoadCache(info, name string, sizeBytes int) *fastcache.Cac
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) mustSaveCache(c *fastcache.Cache, info, name string) {
|
func (s *Storage) mustSaveAndStopCache(c *workingsetcache.Cache, info, name string) {
|
||||||
gomaxprocs := runtime.GOMAXPROCS(-1)
|
|
||||||
path := s.cachePath + "/" + name
|
path := s.cachePath + "/" + name
|
||||||
logger.Infof("saving %s cache to %q...", info, path)
|
logger.Infof("saving %s cache to %q...", info, path)
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
if err := c.SaveToFileConcurrent(path, gomaxprocs); err != nil {
|
if err := c.Save(path); err != nil {
|
||||||
logger.Panicf("FATAL: cannot save %s cache to %q: %s", info, path, err)
|
logger.Panicf("FATAL: cannot save %s cache to %q: %s", info, path, err)
|
||||||
}
|
}
|
||||||
var cs fastcache.Stats
|
var cs fastcache.Stats
|
||||||
c.UpdateStats(&cs)
|
c.UpdateStats(&cs)
|
||||||
c.Reset()
|
c.Stop()
|
||||||
logger.Infof("saved %s cache to %q in %s; entriesCount: %d; sizeBytes: %d",
|
logger.Infof("saved %s cache to %q in %s; entriesCount: %d; sizeBytes: %d",
|
||||||
info, path, time.Since(startTime), cs.EntriesCount, cs.BytesSize)
|
info, path, time.Since(startTime), cs.EntriesCount, cs.BytesSize)
|
||||||
}
|
}
|
||||||
|
@ -971,7 +971,7 @@ func (s *Storage) putTSIDToCache(tsid *TSID, metricName []byte) {
|
||||||
s.tsidCache.Set(metricName, buf)
|
s.tsidCache.Set(metricName, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func openIndexDBTables(path string, metricIDCache, metricNameCache *fastcache.Cache, currHourMetricIDs, prevHourMetricIDs *atomic.Value) (curr, prev *indexDB, err error) {
|
func openIndexDBTables(path string, metricIDCache, metricNameCache *workingsetcache.Cache, currHourMetricIDs, prevHourMetricIDs *atomic.Value) (curr, prev *indexDB, err error) {
|
||||||
if err := fs.MkdirAllIfNotExist(path); err != nil {
|
if err := fs.MkdirAllIfNotExist(path); err != nil {
|
||||||
return nil, nil, fmt.Errorf("cannot create directory %q: %s", path, err)
|
return nil, nil, fmt.Errorf("cannot create directory %q: %s", path, err)
|
||||||
}
|
}
|
||||||
|
|
209
lib/workingsetcache/cache.go
Normal file
209
lib/workingsetcache/cache.go
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
package workingsetcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/fastcache"
|
||||||
|
)
|
||||||
|
|
||||||
|
var oldBehavior = flag.Bool("cache.oldBehavior", false, "Whether to use old behaviour for caches. Old behavior can give better resuts "+
|
||||||
|
"for low-RAM systems serving big number of time series. Systems with enough RAM would consume more RAM when `-cache.oldBehavior` is enabled")
|
||||||
|
|
||||||
|
// Cache is a cache for working set entries.
|
||||||
|
//
|
||||||
|
// The cache evicts inactive entries after the given expireDuration.
|
||||||
|
// Recently accessed entries survive expireDuration.
|
||||||
|
//
|
||||||
|
// Comparing to fastcache, this cache minimizes the required RAM size
|
||||||
|
// to values smaller than maxBytes.
|
||||||
|
type Cache struct {
|
||||||
|
curr atomic.Value
|
||||||
|
prev atomic.Value
|
||||||
|
|
||||||
|
wg sync.WaitGroup
|
||||||
|
stopCh chan struct{}
|
||||||
|
|
||||||
|
misses uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load loads the cache from filePath and limits its size to maxBytes
|
||||||
|
// and evicts inactive entires after expireDuration.
|
||||||
|
//
|
||||||
|
// Stop must be called on the returned cache when it is no longer needed.
|
||||||
|
func Load(filePath string, maxBytes int, expireDuration time.Duration) *Cache {
|
||||||
|
if !*oldBehavior {
|
||||||
|
// Split maxBytes between curr and prev caches.
|
||||||
|
maxBytes /= 2
|
||||||
|
}
|
||||||
|
curr := fastcache.LoadFromFileOrNew(filePath, maxBytes)
|
||||||
|
return newWorkingSetCache(curr, maxBytes, expireDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates new cache with the given maxBytes size and the given expireDuration
|
||||||
|
// for inactive entries.
|
||||||
|
//
|
||||||
|
// Stop must be called on the returned cache when it is no longer needed.
|
||||||
|
func New(maxBytes int, expireDuration time.Duration) *Cache {
|
||||||
|
if !*oldBehavior {
|
||||||
|
// Split maxBytes between curr and prev caches.
|
||||||
|
maxBytes /= 2
|
||||||
|
}
|
||||||
|
curr := fastcache.New(maxBytes)
|
||||||
|
return newWorkingSetCache(curr, maxBytes, expireDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWorkingSetCache(curr *fastcache.Cache, maxBytes int, expireDuration time.Duration) *Cache {
|
||||||
|
prev := fastcache.New(1024)
|
||||||
|
var c Cache
|
||||||
|
c.curr.Store(curr)
|
||||||
|
c.prev.Store(prev)
|
||||||
|
c.stopCh = make(chan struct{})
|
||||||
|
c.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer c.wg.Done()
|
||||||
|
t := time.NewTicker(expireDuration / 2)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-c.stopCh:
|
||||||
|
return
|
||||||
|
case <-t.C:
|
||||||
|
}
|
||||||
|
if *oldBehavior {
|
||||||
|
// Keep the curr cache for old behavior.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not reuse prev cache, since it can have too big capacity.
|
||||||
|
prev := c.prev.Load().(*fastcache.Cache)
|
||||||
|
prev.Reset()
|
||||||
|
curr := c.curr.Load().(*fastcache.Cache)
|
||||||
|
c.prev.Store(curr)
|
||||||
|
curr = fastcache.New(maxBytes)
|
||||||
|
c.curr.Store(curr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save safes the cache to filePath.
|
||||||
|
func (c *Cache) Save(filePath string) error {
|
||||||
|
curr := c.curr.Load().(*fastcache.Cache)
|
||||||
|
concurrency := runtime.GOMAXPROCS(-1)
|
||||||
|
return curr.SaveToFileConcurrent(filePath, concurrency)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the cache.
|
||||||
|
//
|
||||||
|
// The cache cannot be used after the Stop call.
|
||||||
|
func (c *Cache) Stop() {
|
||||||
|
close(c.stopCh)
|
||||||
|
c.wg.Wait()
|
||||||
|
|
||||||
|
c.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets the cache.
|
||||||
|
func (c *Cache) Reset() {
|
||||||
|
prev := c.prev.Load().(*fastcache.Cache)
|
||||||
|
prev.Reset()
|
||||||
|
curr := c.curr.Load().(*fastcache.Cache)
|
||||||
|
curr.Reset()
|
||||||
|
|
||||||
|
c.misses = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateStats updates fcs with cache stats.
|
||||||
|
func (c *Cache) UpdateStats(fcs *fastcache.Stats) {
|
||||||
|
curr := c.curr.Load().(*fastcache.Cache)
|
||||||
|
fcsOrig := *fcs
|
||||||
|
curr.UpdateStats(fcs)
|
||||||
|
if *oldBehavior {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fcs.Misses = fcsOrig.Misses + atomic.LoadUint64(&c.misses)
|
||||||
|
|
||||||
|
fcsOrig.Reset()
|
||||||
|
prev := c.prev.Load().(*fastcache.Cache)
|
||||||
|
prev.UpdateStats(&fcsOrig)
|
||||||
|
fcs.EntriesCount += fcsOrig.EntriesCount
|
||||||
|
fcs.BytesSize += fcsOrig.BytesSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get appends the found value for the given key to dst and returns the result.
|
||||||
|
func (c *Cache) Get(dst, key []byte) []byte {
|
||||||
|
curr := c.curr.Load().(*fastcache.Cache)
|
||||||
|
result := curr.Get(dst, key)
|
||||||
|
if len(result) > len(dst) {
|
||||||
|
// Fast path - the entry is found in the current cache.
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if *oldBehavior {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for the entry in the previous cache.
|
||||||
|
prev := c.prev.Load().(*fastcache.Cache)
|
||||||
|
result = prev.Get(dst, key)
|
||||||
|
if len(result) <= len(dst) {
|
||||||
|
// Nothing found.
|
||||||
|
atomic.AddUint64(&c.misses, 1)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
// Cache the found entry in the current cache.
|
||||||
|
curr.Set(key, result[len(dst):])
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has verifies whether the cahce contains the given key.
|
||||||
|
func (c *Cache) Has(key []byte) bool {
|
||||||
|
curr := c.curr.Load().(*fastcache.Cache)
|
||||||
|
if curr.Has(key) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if *oldBehavior {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
prev := c.prev.Load().(*fastcache.Cache)
|
||||||
|
return prev.Has(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the given value for the given key.
|
||||||
|
func (c *Cache) Set(key, value []byte) {
|
||||||
|
curr := c.curr.Load().(*fastcache.Cache)
|
||||||
|
curr.Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBig appends the found value for the given key to dst and returns the result.
|
||||||
|
func (c *Cache) GetBig(dst, key []byte) []byte {
|
||||||
|
curr := c.curr.Load().(*fastcache.Cache)
|
||||||
|
result := curr.GetBig(dst, key)
|
||||||
|
if len(result) > len(dst) {
|
||||||
|
// Fast path - the entry is found in the current cache.
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if *oldBehavior {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for the entry in the previous cache.
|
||||||
|
prev := c.prev.Load().(*fastcache.Cache)
|
||||||
|
result = prev.GetBig(dst, key)
|
||||||
|
if len(result) <= len(dst) {
|
||||||
|
// Nothing found.
|
||||||
|
atomic.AddUint64(&c.misses, 1)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
// Cache the found entry in the current cache.
|
||||||
|
curr.SetBig(key, result[len(dst):])
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBig sets the given value for the given key.
|
||||||
|
func (c *Cache) SetBig(key, value []byte) {
|
||||||
|
curr := c.curr.Load().(*fastcache.Cache)
|
||||||
|
curr.SetBig(key, value)
|
||||||
|
}
|
Loading…
Reference in a new issue