VictoriaMetrics/lib/lrucache/lrucache_test.go
Nikolay 4cf6219e07
lib/{storage,regexpcache}: replaces regexpCacheMap with LRU cache (#2293)
* lib/{storage,regexpcache}: replaces regexpCacheMap with LRU cache

It should decrease memory usage for regexp caching
with storing cacheEntry by pointer - golang map should be able to effectivly shrink it's size
original issue with this case - unexpected map grows and storage OOM

Apply suggestions from code review

Co-authored-by: Roman Khavronenko <roman@victoriametrics.com>

Adds missing metrics for regexp cache and regexpPrefixes cache

* wip

* wip

Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
2022-03-26 12:57:27 +02:00

126 lines
3.5 KiB
Go

package lrucache
import (
"fmt"
"sync"
"testing"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
)
func TestCache(t *testing.T) {
sizeMaxBytes := 64 * 1024
// Multiply sizeMaxBytes by the square of available CPU cores
// in order to get proper distribution of sizes between cache shards.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2204
cpus := cgroup.AvailableCPUs()
sizeMaxBytes *= cpus * cpus
getMaxSize := func() int {
return sizeMaxBytes
}
c := NewCache(getMaxSize)
defer c.MustStop()
if n := c.SizeBytes(); n != 0 {
t.Fatalf("unexpected SizeBytes(); got %d; want %d", n, 0)
}
if n := c.SizeMaxBytes(); n != sizeMaxBytes {
t.Fatalf("unexpected SizeMaxBytes(); got %d; want %d", n, sizeMaxBytes)
}
k := "foobar"
var e testEntry
entrySize := e.SizeBytes()
// Put a single entry into cache
c.PutEntry(k, &e)
if n := c.Len(); n != 1 {
t.Fatalf("unexpected number of items in the cache; got %d; want %d", n, 1)
}
if n := c.SizeBytes(); n != entrySize {
t.Fatalf("unexpected SizeBytes(); got %d; want %d", n, entrySize)
}
if n := c.Requests(); n != 0 {
t.Fatalf("unexpected number of requests; got %d; want %d", n, 0)
}
if n := c.Misses(); n != 0 {
t.Fatalf("unexpected number of misses; got %d; want %d", n, 0)
}
// Obtain this entry from the cache
if e1 := c.GetEntry(k); e1 != &e {
t.Fatalf("unexpected entry obtained; got %v; want %v", e1, &e)
}
if n := c.Requests(); n != 1 {
t.Fatalf("unexpected number of requests; got %d; want %d", n, 1)
}
if n := c.Misses(); n != 0 {
t.Fatalf("unexpected number of misses; got %d; want %d", n, 0)
}
// Obtain non-existing entry from the cache
if e1 := c.GetEntry("non-existing-key"); e1 != nil {
t.Fatalf("unexpected non-nil block obtained for non-existing key: %v", e1)
}
if n := c.Requests(); n != 2 {
t.Fatalf("unexpected number of requests; got %d; want %d", n, 2)
}
if n := c.Misses(); n != 1 {
t.Fatalf("unexpected number of misses; got %d; want %d", n, 1)
}
// Store the entry again.
c.PutEntry(k, &e)
if n := c.SizeBytes(); n != entrySize {
t.Fatalf("unexpected SizeBytes(); got %d; want %d", n, entrySize)
}
if e1 := c.GetEntry(k); e1 != &e {
t.Fatalf("unexpected entry obtained; got %v; want %v", e1, &e)
}
if n := c.Requests(); n != 3 {
t.Fatalf("unexpected number of requests; got %d; want %d", n, 3)
}
if n := c.Misses(); n != 1 {
t.Fatalf("unexpected number of misses; got %d; want %d", n, 1)
}
// Manually clean the cache. The entry shouldn't be deleted because it was recently accessed.
c.cleanByTimeout()
if n := c.SizeBytes(); n != entrySize {
t.Fatalf("unexpected SizeBytes(); got %d; want %d", n, entrySize)
}
}
func TestCacheConcurrentAccess(t *testing.T) {
const sizeMaxBytes = 16 * 1024 * 1024
getMaxSize := func() int {
return sizeMaxBytes
}
c := NewCache(getMaxSize)
defer c.MustStop()
workers := 5
var wg sync.WaitGroup
wg.Add(workers)
for i := 0; i < workers; i++ {
go func(worker int) {
defer wg.Done()
testCacheSetGet(c, worker)
}(i)
}
wg.Wait()
}
func testCacheSetGet(c *Cache, worker int) {
for i := 0; i < 1000; i++ {
e := testEntry{}
k := fmt.Sprintf("key_%d_%d", worker, i)
c.PutEntry(k, &e)
if e1 := c.GetEntry(k); e1 != &e {
panic(fmt.Errorf("unexpected entry obtained; got %v; want %v", e1, &e))
}
if e1 := c.GetEntry("non-existing-key"); e1 != nil {
panic(fmt.Errorf("unexpected non-nil entry obtained: %v", e1))
}
}
}
type testEntry struct{}
func (tb *testEntry) SizeBytes() int {
return 42
}