Files
metrics/lib/storage/date_metric_id_cache_synctest_test.go
Artem FetishevandGitHub ce45fa4f4a lib/storage: shard dateMetricIDCache (#10486)
Use the same sharded implementation as in metricIDCache. The change is
basically a copy-paste. The only difference is that the rotation period
remains `1h` instead `1m` in order not to break the fix for #10064.

Signed-off-by: Artem Fetishev <[email protected]>
2026-02-18 18:16:37 +01:00

128 lines
3.3 KiB
Go

//go:build synctest
package storage
import (
"testing"
"testing/synctest"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
func (c *dateMetricIDCache) numShards() uint64 {
return uint64(len(c.shards))
}
func (c *dateMetricIDCache) fullRotationPeriod() time.Duration {
return dateMetricIDCacheShardCount * c.rotationPeriod
}
func TestDateMetricIDCache_ClearedWhenUnused(t *testing.T) {
// Entries that are added to the cache but then never retrieved will be
// eventually removed from it.
synctest.Test(t, func(t *testing.T) {
c := newDateMetricIDCache()
defer c.MustStop()
c.Set(123, 456)
// It takes 3 full rotation cycles for an entry to be evicted from the
// cache:
// [in next] -rotation1-> [in curr] -rotation2-> [in prev] -rotation3-> evicted.
time.Sleep(3 * c.fullRotationPeriod())
if c.Has(123, 456) {
t.Fatalf("entry is still in cache")
}
})
// Entries that are added to the cache and retrieved but then never
// retrieved again will be eventually removed from it.
synctest.Test(t, func(t *testing.T) {
c := newDateMetricIDCache()
defer c.MustStop()
c.Set(123, 456)
time.Sleep(c.fullRotationPeriod())
if !c.Has(123, 456) {
t.Fatalf("entry not in cache")
}
time.Sleep(3 * c.fullRotationPeriod())
if c.Has(123, 456) {
t.Fatalf("entry is still in cache")
}
})
// Entries that are added to the cache and then periodically retrieved,
// will remain in cache indefinitely.
synctest.Test(t, func(t *testing.T) {
c := newDateMetricIDCache()
defer c.MustStop()
c.Set(123, 456)
for range 10_000 {
time.Sleep(c.fullRotationPeriod())
if !c.Has(123, 456) {
t.Fatalf("entry not in cache")
}
}
})
}
func TestDateMetricIDCache_Stats(t *testing.T) {
assertStats := func(t *testing.T, c *dateMetricIDCache, want dateMetricIDCacheStats) {
t.Helper()
if diff := cmp.Diff(want, c.Stats(), cmpopts.IgnoreFields(dateMetricIDCacheStats{}, "SizeBytes")); diff != "" {
t.Fatalf("unexpected stats (-want, +got):\n%s", diff)
}
}
const (
date = 12345
numMetricIDs = 16 * 65536
)
synctest.Test(t, func(t *testing.T) {
c := newDateMetricIDCache()
defer c.MustStop()
// Check stats right after the creation.
assertStats(t, c, dateMetricIDCacheStats{})
// Add metricIDs and check stats.
// At this point, all metricIDs are in next.
for metricID := range uint64(numMetricIDs) {
c.Set(date, metricID)
}
assertStats(t, c, dateMetricIDCacheStats{
Size: numMetricIDs,
})
// Get all metricIDs and check stats.
// All metricIDs will be sync'ed from next to curr.
for metricID := range uint64(numMetricIDs) {
if !c.Has(date, metricID) {
t.Fatalf("metricID not in cache: %d", metricID)
}
}
assertStats(t, c, dateMetricIDCacheStats{
Size: numMetricIDs,
SyncsCount: c.numShards(),
})
// Wait until all groups are rotated.
// curr metricIDs will be moved to prev.
time.Sleep(c.fullRotationPeriod() + time.Second)
assertStats(t, c, dateMetricIDCacheStats{
Size: numMetricIDs,
SyncsCount: c.numShards(),
RotationsCount: c.numShards(),
})
// Wait until all groups are rotated.
// The cache now should be empty.
time.Sleep(c.fullRotationPeriod())
assertStats(t, c, dateMetricIDCacheStats{
SyncsCount: c.numShards(),
RotationsCount: 2 * c.numShards(),
})
})
}