Compare commits

...

2 Commits

Author SHA1 Message Date
Maksim Kotlyar
f03292255e upd fastcache 2025-06-03 11:00:03 +03:00
Robin Hayer
186feb34fb lib/workingsetcache: log error when restoring cache from file
Co-authored-by: Roman Khavronenko <hagen1778@gmail.com>
2025-06-02 16:19:59 +03:00
6 changed files with 144 additions and 12 deletions

2
go.mod
View File

@@ -33,7 +33,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1
github.com/VictoriaMetrics/easyproto v0.1.4
github.com/VictoriaMetrics/fastcache v1.12.2
github.com/VictoriaMetrics/fastcache v1.12.5-0.20250603074328-49a009e6aa7e
github.com/VictoriaMetrics/metrics v1.36.0
github.com/VictoriaMetrics/metricsql v0.84.5
github.com/aws/aws-sdk-go-v2 v1.36.3

7
go.sum
View File

@@ -38,8 +38,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc=
github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
github.com/VictoriaMetrics/fastcache v1.12.5-0.20250603074328-49a009e6aa7e h1:/bKjVzePOGQFGlG4QtyK0lMXqtfysR4dtvCRgcIi7vo=
github.com/VictoriaMetrics/fastcache v1.12.5-0.20250603074328-49a009e6aa7e/go.mod h1:K+JGPBn0sueFlLjZ8rcVM0cKkWKNElKyQXmw57QOoYI=
github.com/VictoriaMetrics/metrics v1.36.0 h1:f3SZMpLgIG4hJm2zfDs6wicxQ/QNWBZekY5rEGgbHKs=
github.com/VictoriaMetrics/metrics v1.36.0/go.mod h1:r7hveu6xMdUACXvB8TYdAj8WEsKzWB0EkpJN+RDtOf8=
github.com/VictoriaMetrics/metricsql v0.84.5 h1:3JeIKpEh9yCNBVoeKJovICRvNea6h6m50h/RGW36P2g=
@@ -98,7 +98,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb/v3 v3.1.7 h1:2FsIW307kt7A/rz/ZI2lvPO+v3wKazzE4K/0LtTWsOI=
@@ -169,7 +168,6 @@ github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeD
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
@@ -473,7 +471,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=

View File

@@ -1,13 +1,17 @@
package workingsetcache
import (
"errors"
"flag"
"os"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/timeutil"
"github.com/VictoriaMetrics/fastcache"
)
@@ -64,8 +68,29 @@ func Load(filePath string, maxBytes int) *Cache {
return loadWithExpire(filePath, maxBytes, *cacheExpireDuration)
}
// loadFromFileOrNew attempts to load a fastcache.Cache from the given file path
// If loading fails due to an error (e.g. corrupted or unreadable file), the error is logged
// and a new cache is created with the specified maxBytes size.
func loadFromFileOrNew(filePath string, maxBytes int) *fastcache.Cache {
cache, err := fastcache.LoadFromFileMaxBytes(filePath, maxBytes)
if err == nil {
return cache
}
if errors.Is(err, os.ErrNotExist) {
logger.Infof("cache at path %s missing files; init new cache", filePath)
} else if strings.Contains(err.Error(), "contains maxBytes") {
// covers the cache reset due to max memory size change at
// https://github.com/VictoriaMetrics/fastcache/blob/198c85ee90a1f65127126b5904c191e70f083cbf/file.go#L133
logger.Warnf("%s; init new cache", err)
} else {
logger.Errorf("cache at path %s is invalid: %s; init new cache", filePath, err)
}
return fastcache.New(maxBytes)
}
func loadWithExpire(filePath string, maxBytes int, expireDuration time.Duration) *Cache {
curr := fastcache.LoadFromFileOrNew(filePath, maxBytes)
curr := loadFromFileOrNew(filePath, maxBytes)
var cs fastcache.Stats
curr.UpdateStats(&cs)
if cs.EntriesCount == 0 {
@@ -75,7 +100,7 @@ func loadWithExpire(filePath string, maxBytes int, expireDuration time.Duration)
// Try loading it again with maxBytes / 2 size.
// Put the loaded cache into `prev` instead of `curr`
// in order to limit the growth of the cache for the current period of time.
prev := fastcache.LoadFromFileOrNew(filePath, maxBytes/2)
prev := loadFromFileOrNew(filePath, maxBytes/2)
curr := fastcache.New(maxBytes / 2)
c := newCacheInternal(curr, prev, split, maxBytes)
c.runWatchers(expireDuration)

View File

@@ -0,0 +1,100 @@
package workingsetcache
import (
"bytes"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/fastcache"
)
func TestLoadFromFileOrNewError(t *testing.T) {
defer os.RemoveAll(t.Name())
f := func(path string, expErr string) {
logBuffer := &bytes.Buffer{}
logger.SetOutputForTests(logBuffer)
defer logger.ResetOutputForTest()
cache := loadFromFileOrNew(path, 10000)
if cache == nil {
t.Fatal("expected a new cache instance, got nil")
}
testCacheEntriesEqual(t, cache, 0)
if !strings.Contains(logBuffer.String(), expErr) {
t.Fatalf("expected log message not found; got: %s", logBuffer.String())
}
}
f("cacheDirNotExist", "missing files; init new cache")
path := filepath.Join(t.Name(), "workingsetcache", "emptyDir")
if err := os.MkdirAll(path, 0777); err != nil {
t.Fatalf("failed to create cache directory: %v", err)
}
f(path, "missing files; init new cache")
path = initCacheForTest(t, `missingMetadata`, 10000)
if err := os.Remove(filepath.Join(path, `metadata.bin`)); err != nil {
t.Fatalf("failed to remove metadata.bin file: %v", err)
}
f(path, "missing files; init new cache")
path = initCacheForTest(t, `invalidMetadata`, 10000)
if err := os.WriteFile(filepath.Join(path, `metadata.bin`), []byte(""), 0644); err != nil {
t.Fatalf("failed to write test metadata file: %v", err)
}
f(path, "invalid: cannot read maxBucketChunks")
path = initCacheForTest(t, `cacheMismatch`, 87654321)
f(path, "contains maxBytes=10000; want 33554432; init new cache")
}
func TestLoadFromFileOrNewOK(t *testing.T) {
defer os.RemoveAll(t.Name())
cachePath := initCacheForTest(t, `ok`, 10000)
cache := loadFromFileOrNew(cachePath, 10000)
if cache == nil {
t.Fatal("expected a new cache instance, got nil")
}
testCacheEntriesEqual(t, cache, 200)
actualVal := cache.Get(nil, []byte("foo1"))
if !bytes.Equal(actualVal, []byte("fooVal")) {
t.Fatalf("expected cached value 'fooVal', got %q", actualVal)
}
}
func initCacheForTest(t *testing.T, testCase string, maxBytes int) string {
c := New(maxBytes)
defer c.Stop()
for i := 0; i < 100; i++ {
c.Set([]byte("foo"+strconv.Itoa(i)), []byte("fooVal"))
c.Set([]byte("bar"+strconv.Itoa(i)), []byte("barVal"))
}
path := filepath.Join(t.Name(), "workingsetcache", testCase)
if err := c.Save(path); err != nil {
t.Fatalf("Save error: %s", err)
}
return path
}
func testCacheEntriesEqual(t *testing.T, c *fastcache.Cache, expEntries uint64) {
var s fastcache.Stats
c.UpdateStats(&s)
if s.EntriesCount != expEntries {
t.Fatalf("expected %d entries in cache, got %d", expEntries, s.EntriesCount)
}
}

View File

@@ -76,6 +76,16 @@ func (c *Cache) SaveToFileConcurrent(filePath string, concurrency int) error {
return nil
}
// LoadFromFileMaxBytes loads cache data from the specified filePath,
// enforcing that the cache capacity matches the provided maxBytes value.
//
// Returns an error if the stored cache's capacity differs from maxBytes.
//
// See SaveToFile* for functions that persist cache data to a file.
func LoadFromFileMaxBytes(filePath string, maxBytes int) (*Cache, error) {
return load(filePath, maxBytes)
}
// LoadFromFile loads cache data from the given filePath.
//
// See SaveToFile* for saving cache data to file.
@@ -141,7 +151,7 @@ func load(filePath string, maxBytes int) (*Cache, error) {
// Read bucket files from filePath dir.
d, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("cannot open %q: %s", filePath, err)
return nil, fmt.Errorf("cannot open %q: %w", filePath, err)
}
defer func() {
_ = d.Close()
@@ -206,7 +216,7 @@ func loadMetadata(dir string) (uint64, error) {
metadataPath := dir + "/metadata.bin"
metadataFile, err := os.Open(metadataPath)
if err != nil {
return 0, fmt.Errorf("cannot open %q: %s", metadataPath, err)
return 0, fmt.Errorf("cannot open %q: %w", metadataPath, err)
}
defer func() {
_ = metadataFile.Close()

4
vendor/modules.txt vendored
View File

@@ -114,8 +114,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go/apps/public
# github.com/VictoriaMetrics/easyproto v0.1.4
## explicit; go 1.18
github.com/VictoriaMetrics/easyproto
# github.com/VictoriaMetrics/fastcache v1.12.2
## explicit; go 1.13
# github.com/VictoriaMetrics/fastcache v1.12.5-0.20250603074328-49a009e6aa7e
## explicit; go 1.24.0
github.com/VictoriaMetrics/fastcache
# github.com/VictoriaMetrics/metrics v1.36.0
## explicit; go 1.17