mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-12 05:32:36 +00:00
Vendored
+96
@@ -1,8 +1,11 @@
|
||||
package libpack_cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
libpack_cache_memory "github.com/lukaszraczylo/graphql-monitoring-proxy/cache/memory"
|
||||
libpack_cache_redis "github.com/lukaszraczylo/graphql-monitoring-proxy/cache/redis"
|
||||
libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging"
|
||||
@@ -117,3 +120,96 @@ func (suite *Tests) Test_cacheLookupRedis() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *Tests) Test_cacheConcurrency() {
|
||||
config = &CacheConfig{
|
||||
Logger: libpack_logger.New(),
|
||||
Client: libpack_cache_memory.New(5 * time.Second),
|
||||
TTL: 5,
|
||||
}
|
||||
|
||||
const numGoroutines = 10
|
||||
const numOperations = 1000
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(numGoroutines)
|
||||
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
for j := 0; j < numOperations; j++ {
|
||||
key := fmt.Sprintf("key-%d-%d", id, j)
|
||||
value := []byte(fmt.Sprintf("value-%d-%d", id, j))
|
||||
CacheStore(key, value)
|
||||
retrieved := CacheLookup(key)
|
||||
assert.Equal(string(value), string(retrieved), "Concurrent cache operation failed")
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// func (suite *Tests) Test_cacheEviction() {
|
||||
// config = &CacheConfig{
|
||||
// Logger: libpack_logger.New(),
|
||||
// Client: libpack_cache_memory.New(3 * time.Second), // 3 seconds TTL
|
||||
// TTL: 3,
|
||||
// }
|
||||
|
||||
// // Fill the cache
|
||||
// for i := 0; i < 20; i++ {
|
||||
// key := fmt.Sprintf("key-%d", i)
|
||||
// value := []byte(fmt.Sprintf("value-%d", i))
|
||||
// CacheStore(key, value)
|
||||
// time.Sleep(100 * time.Millisecond) // Ensure different creation times
|
||||
// }
|
||||
|
||||
// // Wait for the TTL to expire for the first half of the items
|
||||
// time.Sleep(3100 * time.Millisecond)
|
||||
|
||||
// // Check that the oldest items have been evicted
|
||||
// for i := 0; i < 10; i++ {
|
||||
// key := fmt.Sprintf("key-%d", i)
|
||||
// retrieved := CacheLookup(key)
|
||||
// assert.Nil(retrieved, fmt.Sprintf("Old item %s should have been evicted", key))
|
||||
// }
|
||||
|
||||
// // Check that the newer items are still in the cache
|
||||
// for i := 10; i < 20; i++ {
|
||||
// key := fmt.Sprintf("key-%d", i)
|
||||
// expected := []byte(fmt.Sprintf("value-%d", i))
|
||||
// retrieved := CacheLookup(key)
|
||||
// assert.Equal(expected, retrieved, fmt.Sprintf("Recent item %s should be in cache", key))
|
||||
// }
|
||||
// }
|
||||
|
||||
func (suite *Tests) Test_cacheRedisFailure() {
|
||||
mr, err := miniredis.Run()
|
||||
if err != nil {
|
||||
suite.T().Fatal(err)
|
||||
}
|
||||
defer mr.Close()
|
||||
|
||||
config = &CacheConfig{
|
||||
Logger: libpack_logger.New(),
|
||||
Client: libpack_cache_redis.New(&libpack_cache_redis.RedisClientConfig{
|
||||
RedisServer: mr.Addr(),
|
||||
RedisDB: 0,
|
||||
}),
|
||||
TTL: 5,
|
||||
}
|
||||
|
||||
// Test normal operation
|
||||
CacheStore("test-key", []byte("test-value"))
|
||||
retrieved := CacheLookup("test-key")
|
||||
assert.Equal([]byte("test-value"), retrieved)
|
||||
|
||||
// Simulate Redis failure
|
||||
mr.Close()
|
||||
|
||||
// Operations should not panic, but should return errors or nil values
|
||||
CacheStore("another-key", []byte("another-value"))
|
||||
retrieved = CacheLookup("another-key")
|
||||
assert.Nil(retrieved, "Lookup should return nil when Redis is down")
|
||||
}
|
||||
|
||||
Vendored
+9
-3
@@ -69,11 +69,17 @@ func (c *Cache) Set(key string, value []byte, ttl time.Duration) {
|
||||
|
||||
func (c *Cache) Get(key string) ([]byte, bool) {
|
||||
entry, ok := c.entries.Load(key)
|
||||
if !ok || entry.(CacheEntry).ExpiresAt.Before(time.Now()) {
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
compressedValue := entry.(CacheEntry).Value
|
||||
value, err := c.decompress(compressedValue)
|
||||
|
||||
cacheEntry := entry.(CacheEntry)
|
||||
if cacheEntry.ExpiresAt.Before(time.Now()) {
|
||||
c.entries.Delete(key)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
value, err := c.decompress(cacheEntry.Value)
|
||||
if err != nil {
|
||||
log.Printf("Error decompressing value for key %s: %v", key, err)
|
||||
return nil, false
|
||||
|
||||
Vendored
+28
@@ -1,6 +1,7 @@
|
||||
package libpack_cache_memory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -52,3 +53,30 @@ func BenchmarkMemCacheStats(b *testing.B) {
|
||||
cache.Set(key, value, 5*time.Second) // Pre-set a value to retrieve
|
||||
cache.Get(key)
|
||||
}
|
||||
|
||||
func BenchmarkCacheSet(b *testing.B) {
|
||||
cache := New(5 * time.Second)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cache.Set(fmt.Sprintf("key-%d", i), []byte("value"), 5*time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCacheGet(b *testing.B) {
|
||||
cache := New(5 * time.Second)
|
||||
cache.Set("test-key", []byte("test-value"), 5*time.Second)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cache.Get("test-key")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCacheDelete(b *testing.B) {
|
||||
cache := New(5 * time.Second)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
key := fmt.Sprintf("key-%d", i)
|
||||
cache.Set(key, []byte("value"), 5*time.Second)
|
||||
cache.Delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+56
@@ -1,6 +1,8 @@
|
||||
package libpack_cache_memory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -110,3 +112,57 @@ func (suite *MemoryTestSuite) Test_CacheExpire() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *MemoryTestSuite) Test_ConcurrentReadWrite() {
|
||||
cache := New(5 * time.Second)
|
||||
const numGoroutines = 100
|
||||
const numOperations = 1000
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(numGoroutines)
|
||||
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
for j := 0; j < numOperations; j++ {
|
||||
key := fmt.Sprintf("key-%d-%d", id, j)
|
||||
value := []byte(fmt.Sprintf("value-%d-%d", id, j))
|
||||
|
||||
if j%2 == 0 {
|
||||
cache.Set(key, value, 5*time.Second)
|
||||
} else {
|
||||
_, _ = cache.Get(key)
|
||||
}
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (suite *MemoryTestSuite) Test_LargeItems() {
|
||||
cache := New(5 * time.Second)
|
||||
largeValue := make([]byte, 10*1024*1024) // 10MB
|
||||
cache.Set("large-key", largeValue, 5*time.Second)
|
||||
|
||||
retrieved, found := cache.Get("large-key")
|
||||
suite.Assert().True(found)
|
||||
suite.Assert().Equal(largeValue, retrieved)
|
||||
}
|
||||
|
||||
func (suite *MemoryTestSuite) Test_ZeroTTL() {
|
||||
cache := New(5 * time.Second)
|
||||
cache.Set("zero-ttl", []byte("value"), 0)
|
||||
|
||||
_, found := cache.Get("zero-ttl")
|
||||
suite.Assert().False(found, "Item with zero TTL should not be stored")
|
||||
}
|
||||
|
||||
func (suite *MemoryTestSuite) Test_LongTTL() {
|
||||
cache := New(5 * time.Second)
|
||||
cache.Set("long-ttl", []byte("value"), 24*365*time.Hour) // 1 year
|
||||
|
||||
retrieved, found := cache.Get("long-ttl")
|
||||
suite.Assert().True(found)
|
||||
suite.Assert().Equal([]byte("value"), retrieved)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user