mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-05-17 08:36:55 +03:00
There are cases then the key sizeBytes is much greater than the value sizeBytes. Therefore it is important to include the key sizeBytes into the total. Also fix some code comments. Signed-off-by: Artem Fetishev <rtm@victoriametrics.com>
160 lines
4.5 KiB
Go
160 lines
4.5 KiB
Go
package lrucache
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
|
|
)
|
|
|
|
func TestCache(t *testing.T) {
|
|
sizeMaxBytes := uint64(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 *= uint64(cpus * cpus)
|
|
getMaxSize := func() uint64 {
|
|
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)
|
|
}
|
|
if n := c.Resets(); n != 0 {
|
|
t.Fatalf("unexpected Resets(); got %d; want %d", n, 0)
|
|
}
|
|
k := "foobar"
|
|
var e testEntry
|
|
keySize := uint64(len(k))
|
|
entrySize := e.SizeBytes()
|
|
keyEntrySize := keySize + entrySize
|
|
// 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 != keyEntrySize {
|
|
t.Fatalf("unexpected SizeBytes(); got %d; want %d", n, keyEntrySize)
|
|
}
|
|
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)
|
|
}
|
|
if n := c.Resets(); n != 0 {
|
|
t.Fatalf("unexpected Resets(); 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)
|
|
}
|
|
if n := c.Resets(); n != 0 {
|
|
t.Fatalf("unexpected Resets(); 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)
|
|
}
|
|
if n := c.Resets(); n != 0 {
|
|
t.Fatalf("unexpected Resets(); got %d; want %d", n, 0)
|
|
}
|
|
// Store the entry again.
|
|
c.PutEntry(k, &e)
|
|
if n := c.SizeBytes(); n != keyEntrySize {
|
|
t.Fatalf("unexpected SizeBytes(); got %d; want %d", n, keyEntrySize)
|
|
}
|
|
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)
|
|
}
|
|
if n := c.Resets(); n != 0 {
|
|
t.Fatalf("unexpected Resets(); got %d; want %d", n, 0)
|
|
}
|
|
|
|
// Manually clean the cache. The entry shouldn't be deleted because it was recently accessed.
|
|
c.cleanByTimeout()
|
|
if n := c.SizeBytes(); n != keyEntrySize {
|
|
t.Fatalf("unexpected SizeBytes(); got %d; want %d", n, keyEntrySize)
|
|
}
|
|
|
|
// Reset cache.
|
|
c.Reset()
|
|
if n := c.SizeMaxBytes(); n != sizeMaxBytes {
|
|
t.Fatalf("unexpected SizeMaxBytes(); got %d; want %d", n, sizeMaxBytes)
|
|
}
|
|
if n := c.SizeBytes(); n != 0 {
|
|
t.Fatalf("unexpected SizeBytes(); got %d; want %d", n, 0)
|
|
}
|
|
if n := c.Requests(); n != 3 {
|
|
t.Fatalf("unexpected number of requests; got %d; want %d", n, 0)
|
|
}
|
|
if n := c.Misses(); n != 1 {
|
|
t.Fatalf("unexpected number of misses; got %d; want %d", n, 0)
|
|
}
|
|
if n := c.Resets(); n != 1 {
|
|
t.Fatalf("unexpected Resets(); got %d; want %d", n, 1)
|
|
}
|
|
}
|
|
|
|
func TestCacheConcurrentAccess(_ *testing.T) {
|
|
const sizeMaxBytes = uint64(16 * 1024 * 1024)
|
|
getMaxSize := func() uint64 {
|
|
return sizeMaxBytes
|
|
}
|
|
c := NewCache(getMaxSize)
|
|
defer c.MustStop()
|
|
|
|
workers := 5
|
|
var wg sync.WaitGroup
|
|
for worker := range workers {
|
|
wg.Go(func() {
|
|
testCacheSetGet(c, worker)
|
|
})
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func testCacheSetGet(c *Cache, worker int) {
|
|
for i := range 1000 {
|
|
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() uint64 {
|
|
return 42
|
|
}
|