mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 15:36:48 +00:00
220 lines
6.4 KiB
Go
220 lines
6.4 KiB
Go
package memorycache
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/SigNoz/signoz/pkg/cache"
|
|
"github.com/SigNoz/signoz/pkg/factory/factorytest"
|
|
"github.com/SigNoz/signoz/pkg/types/cachetypes"
|
|
"github.com/SigNoz/signoz/pkg/valuer"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type CloneableA struct {
|
|
Key string
|
|
Value int
|
|
Expiry time.Duration
|
|
}
|
|
|
|
func (cloneable *CloneableA) Clone() cachetypes.Cacheable {
|
|
return &CloneableA{
|
|
Key: cloneable.Key,
|
|
Value: cloneable.Value,
|
|
Expiry: cloneable.Expiry,
|
|
}
|
|
}
|
|
|
|
func (cloneable *CloneableA) MarshalBinary() ([]byte, error) {
|
|
return json.Marshal(cloneable)
|
|
}
|
|
|
|
func (cloneable *CloneableA) UnmarshalBinary(data []byte) error {
|
|
return json.Unmarshal(data, cloneable)
|
|
}
|
|
|
|
type CacheableB struct {
|
|
Key string
|
|
Value int
|
|
Expiry time.Duration
|
|
}
|
|
|
|
func (cacheable *CacheableB) MarshalBinary() ([]byte, error) {
|
|
return json.Marshal(cacheable)
|
|
}
|
|
|
|
func (cacheable *CacheableB) UnmarshalBinary(data []byte) error {
|
|
return json.Unmarshal(data, cacheable)
|
|
}
|
|
|
|
func TestCloneableSetWithNilPointer(t *testing.T) {
|
|
cache, err := New(context.Background(), factorytest.NewSettings(), cache.Config{Provider: "memory", Memory: cache.Memory{
|
|
TTL: 10 * time.Second,
|
|
CleanupInterval: 10 * time.Second,
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
var cloneable *CloneableA
|
|
assert.Error(t, cache.Set(context.Background(), valuer.GenerateUUID(), "key", cloneable, 10*time.Second))
|
|
}
|
|
|
|
func TestCacheableSetWithNilPointer(t *testing.T) {
|
|
cache, err := New(context.Background(), factorytest.NewSettings(), cache.Config{Provider: "memory", Memory: cache.Memory{
|
|
TTL: 10 * time.Second,
|
|
CleanupInterval: 10 * time.Second,
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
var cacheable *CacheableB
|
|
assert.Error(t, cache.Set(context.Background(), valuer.GenerateUUID(), "key", cacheable, 10*time.Second))
|
|
}
|
|
|
|
func TestCloneableSetGet(t *testing.T) {
|
|
cache, err := New(context.Background(), factorytest.NewSettings(), cache.Config{Provider: "memory", Memory: cache.Memory{
|
|
TTL: 10 * time.Second,
|
|
CleanupInterval: 10 * time.Second,
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
orgID := valuer.GenerateUUID()
|
|
cloneable := &CloneableA{
|
|
Key: "some-random-key",
|
|
Value: 1,
|
|
Expiry: time.Microsecond,
|
|
}
|
|
|
|
assert.NoError(t, cache.Set(context.Background(), orgID, "key", cloneable, 10*time.Second))
|
|
|
|
provider := cache.(*provider)
|
|
insideCache, found := provider.cc.Get(strings.Join([]string{orgID.StringValue(), "key"}, "::"))
|
|
assert.True(t, found)
|
|
assert.IsType(t, &CloneableA{}, insideCache)
|
|
|
|
cached := new(CloneableA)
|
|
assert.NoError(t, cache.Get(context.Background(), orgID, "key", cached, false))
|
|
|
|
assert.Equal(t, cloneable, cached)
|
|
// confirm that the cached cloneable is a different pointer
|
|
assert.NotSame(t, cloneable, cached)
|
|
}
|
|
|
|
func TestCacheableSetGet(t *testing.T) {
|
|
cache, err := New(context.Background(), factorytest.NewSettings(), cache.Config{Provider: "memory", Memory: cache.Memory{
|
|
TTL: 10 * time.Second,
|
|
CleanupInterval: 10 * time.Second,
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
orgID := valuer.GenerateUUID()
|
|
cacheable := &CacheableB{
|
|
Key: "some-random-key",
|
|
Value: 1,
|
|
Expiry: time.Microsecond,
|
|
}
|
|
|
|
assert.NoError(t, cache.Set(context.Background(), orgID, "key", cacheable, 10*time.Second))
|
|
|
|
provider := cache.(*provider)
|
|
insideCache, found := provider.cc.Get(strings.Join([]string{orgID.StringValue(), "key"}, "::"))
|
|
assert.True(t, found)
|
|
assert.IsType(t, []byte{}, insideCache)
|
|
assert.Equal(t, "{\"Key\":\"some-random-key\",\"Value\":1,\"Expiry\":1000}", string(insideCache.([]byte)))
|
|
|
|
cached := new(CacheableB)
|
|
assert.NoError(t, cache.Get(context.Background(), orgID, "key", cached, false))
|
|
|
|
assert.Equal(t, cacheable, cached)
|
|
assert.NotSame(t, cacheable, cached)
|
|
}
|
|
|
|
func TestGetWithNilPointer(t *testing.T) {
|
|
cache, err := New(context.Background(), factorytest.NewSettings(), cache.Config{Provider: "memory", Memory: cache.Memory{
|
|
TTL: 10 * time.Second,
|
|
CleanupInterval: 10 * time.Second,
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
var cloneable *CloneableA
|
|
assert.Error(t, cache.Get(context.Background(), valuer.GenerateUUID(), "key", cloneable, false))
|
|
}
|
|
|
|
func TestSetGetWithDifferentTypes(t *testing.T) {
|
|
cache, err := New(context.Background(), factorytest.NewSettings(), cache.Config{Provider: "memory", Memory: cache.Memory{
|
|
TTL: 10 * time.Second,
|
|
CleanupInterval: 10 * time.Second,
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
orgID := valuer.GenerateUUID()
|
|
|
|
cloneable := &CloneableA{
|
|
Key: "some-random-key",
|
|
Value: 1,
|
|
Expiry: time.Microsecond,
|
|
}
|
|
assert.NoError(t, cache.Set(context.Background(), orgID, "key", cloneable, 10*time.Second))
|
|
|
|
cachedCacheable := new(CacheableB)
|
|
err = cache.Get(context.Background(), orgID, "key", cachedCacheable, false)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestCloneableConcurrentSetGet(t *testing.T) {
|
|
cache, err := New(context.Background(), factorytest.NewSettings(), cache.Config{Provider: "memory", Memory: cache.Memory{
|
|
TTL: 10 * time.Second,
|
|
CleanupInterval: 10 * time.Second,
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
orgID := valuer.GenerateUUID()
|
|
numGoroutines := 100
|
|
done := make(chan bool, numGoroutines*2)
|
|
cloneables := make([]*CloneableA, numGoroutines)
|
|
mu := sync.Mutex{}
|
|
|
|
for i := 0; i < numGoroutines; i++ {
|
|
go func(id int) {
|
|
cloneable := &CloneableA{
|
|
Key: fmt.Sprintf("key-%d", id),
|
|
Value: id,
|
|
Expiry: 50 * time.Second,
|
|
}
|
|
err := cache.Set(context.Background(), orgID, fmt.Sprintf("key-%d", id), cloneable, 10*time.Second)
|
|
assert.NoError(t, err)
|
|
mu.Lock()
|
|
cloneables[id] = cloneable
|
|
mu.Unlock()
|
|
done <- true
|
|
}(i)
|
|
}
|
|
|
|
for i := 0; i < numGoroutines; i++ {
|
|
go func(id int) {
|
|
cachedCloneable := new(CloneableA)
|
|
err := cache.Get(context.Background(), orgID, fmt.Sprintf("key-%d", id), cachedCloneable, false)
|
|
// Some keys might not exist due to concurrent access, which is expected
|
|
_ = err
|
|
done <- true
|
|
}(i)
|
|
}
|
|
|
|
for i := 0; i < numGoroutines*2; i++ {
|
|
<-done
|
|
}
|
|
|
|
for i := 0; i < numGoroutines; i++ {
|
|
cachedCloneable := new(CloneableA)
|
|
assert.NoError(t, cache.Get(context.Background(), orgID, fmt.Sprintf("key-%d", i), cachedCloneable, false))
|
|
assert.Equal(t, fmt.Sprintf("key-%d", i), cachedCloneable.Key)
|
|
assert.Equal(t, i, cachedCloneable.Value)
|
|
// confirm that the cached cacheable is a different pointer
|
|
assert.NotSame(t, cachedCloneable, cloneables[i])
|
|
}
|
|
}
|