mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-12 05:32:36 +00:00
Update cache library, use miniredis for testing, add additional benchmarks. (#14)
Update cache library, Update logging library, use miniredis for testing, add additional benchmarks.
This commit is contained in:
Vendored
+134
@@ -0,0 +1,134 @@
|
||||
package libpack_cache
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
fiber "github.com/gofiber/fiber/v2"
|
||||
"github.com/gookit/goutil/strutil"
|
||||
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"
|
||||
)
|
||||
|
||||
type CacheConfig struct {
|
||||
Logger *libpack_logger.Logger
|
||||
Client CacheClient
|
||||
Redis struct {
|
||||
Enable bool `json:"enable"`
|
||||
URL string `json:"url"`
|
||||
Password string `json:"password"`
|
||||
DB int `json:"db"`
|
||||
}
|
||||
TTL int `json:"ttl"`
|
||||
}
|
||||
|
||||
type CacheStats struct {
|
||||
CachedQueries int64 `json:"cached_queries"`
|
||||
CacheHits int64 `json:"cache_hits"`
|
||||
CacheMisses int64 `json:"cache_misses"`
|
||||
}
|
||||
|
||||
type CacheClient interface {
|
||||
Set(key string, value []byte, ttl time.Duration)
|
||||
Get(key string) ([]byte, bool)
|
||||
Delete(key string)
|
||||
Clear()
|
||||
CountQueries() int64
|
||||
}
|
||||
|
||||
var (
|
||||
cacheStats *CacheStats
|
||||
config *CacheConfig
|
||||
)
|
||||
|
||||
func CalculateHash(c *fiber.Ctx) string {
|
||||
return strutil.Md5(c.Body())
|
||||
}
|
||||
|
||||
func EnableCache(cfg *CacheConfig) {
|
||||
if cfg.Logger == nil {
|
||||
cfg.Logger = libpack_logger.New()
|
||||
cfg.Logger.Info(&libpack_logger.LogMessage{
|
||||
Message: "Initializing in-module logger",
|
||||
})
|
||||
}
|
||||
cacheStats = &CacheStats{}
|
||||
if ShouldUseRedisCache(cfg) {
|
||||
cfg.Logger.Info(&libpack_logger.LogMessage{
|
||||
Message: "Using Redis cache",
|
||||
})
|
||||
cfg.Client = libpack_cache_redis.New(&libpack_cache_redis.RedisClientConfig{
|
||||
RedisDB: cfg.Redis.DB,
|
||||
RedisServer: cfg.Redis.URL,
|
||||
RedisPassword: cfg.Redis.Password,
|
||||
})
|
||||
} else {
|
||||
cfg.Logger.Info(&libpack_logger.LogMessage{
|
||||
Message: "Using in-memory cache",
|
||||
})
|
||||
cfg.Client = libpack_cache_memory.New(time.Duration(cfg.TTL) * time.Second)
|
||||
}
|
||||
config = cfg
|
||||
}
|
||||
|
||||
func CacheLookup(hash string) []byte {
|
||||
obj, found := config.Client.Get(hash)
|
||||
if found {
|
||||
atomic.AddInt64(&cacheStats.CacheHits, 1)
|
||||
return obj
|
||||
}
|
||||
atomic.AddInt64(&cacheStats.CacheMisses, 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func CacheDelete(hash string) {
|
||||
config.Logger.Debug(&libpack_logger.LogMessage{
|
||||
Message: "Deleting data from cache",
|
||||
Pairs: map[string]interface{}{"hash": hash},
|
||||
})
|
||||
atomic.AddInt64(&cacheStats.CachedQueries, -1)
|
||||
config.Client.Delete(hash)
|
||||
}
|
||||
|
||||
func CacheStore(hash string, data []byte) {
|
||||
config.Logger.Debug(&libpack_logger.LogMessage{
|
||||
Message: "Storing data in cache",
|
||||
Pairs: map[string]interface{}{"hash": hash},
|
||||
})
|
||||
atomic.AddInt64(&cacheStats.CachedQueries, 1)
|
||||
config.Client.Set(hash, data, time.Duration(config.TTL)*time.Second)
|
||||
}
|
||||
|
||||
func CacheStoreWithTTL(hash string, data []byte, ttl time.Duration) {
|
||||
config.Logger.Debug(&libpack_logger.LogMessage{
|
||||
Message: "Storing data in cache with TTL",
|
||||
Pairs: map[string]interface{}{"hash": hash, "ttl": ttl},
|
||||
})
|
||||
atomic.AddInt64(&cacheStats.CachedQueries, 1)
|
||||
config.Client.Set(hash, data, ttl)
|
||||
}
|
||||
|
||||
func CacheGetQueries() int64 {
|
||||
config.Logger.Debug(&libpack_logger.LogMessage{
|
||||
Message: "Counting cache queries",
|
||||
})
|
||||
return config.Client.CountQueries()
|
||||
}
|
||||
|
||||
func CacheClear() {
|
||||
config.Client.Clear()
|
||||
cacheStats = &CacheStats{}
|
||||
}
|
||||
|
||||
func GetCacheStats() *CacheStats {
|
||||
config.Logger.Debug(&libpack_logger.LogMessage{
|
||||
Message: "Getting cache stats",
|
||||
})
|
||||
cacheStats.CachedQueries = CacheGetQueries()
|
||||
return cacheStats
|
||||
}
|
||||
|
||||
func ShouldUseRedisCache(cfg *CacheConfig) bool {
|
||||
return cfg.Redis.Enable
|
||||
}
|
||||
Vendored
+107
@@ -0,0 +1,107 @@
|
||||
package libpack_cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
const (
|
||||
Parallelism = 4
|
||||
RequestPerSec = 10000
|
||||
)
|
||||
|
||||
func BenchmarkCacheLookupInMemory(b *testing.B) {
|
||||
config = &CacheConfig{
|
||||
Logger: libpack_logger.New(),
|
||||
Client: libpack_cache_memory.New(5 * time.Minute),
|
||||
TTL: 5,
|
||||
}
|
||||
|
||||
hash := "00000000000000000000000000000000001337"
|
||||
data := []byte("it's fine.")
|
||||
CacheStore(hash, data)
|
||||
|
||||
b.SetParallelism(Parallelism)
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
CacheLookup(hash)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkCacheLookupRedis(b *testing.B) {
|
||||
mockedCache := libpack_cache_redis.New(&libpack_cache_redis.RedisClientConfig{
|
||||
RedisServer: redisMockServer.Addr(),
|
||||
RedisDB: 0,
|
||||
})
|
||||
|
||||
config = &CacheConfig{
|
||||
Logger: libpack_logger.New(),
|
||||
Client: mockedCache,
|
||||
TTL: 5,
|
||||
}
|
||||
|
||||
hash := "00000000000000000000000000000000001337"
|
||||
data := []byte("it's fine.")
|
||||
CacheStore(hash, data)
|
||||
|
||||
b.SetParallelism(Parallelism)
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
CacheLookup(hash)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkCacheStoreInMemory(b *testing.B) {
|
||||
config = &CacheConfig{
|
||||
Logger: libpack_logger.New(),
|
||||
Client: libpack_cache_memory.New(5 * time.Minute),
|
||||
TTL: 5,
|
||||
}
|
||||
|
||||
hash := "00000000000000000000000000000000001337"
|
||||
data := []byte("it's fine.")
|
||||
|
||||
b.SetParallelism(Parallelism)
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
CacheStore(hash, data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkCacheStoreRedis(b *testing.B) {
|
||||
mockedCache := libpack_cache_redis.New(&libpack_cache_redis.RedisClientConfig{
|
||||
RedisServer: redisMockServer.Addr(),
|
||||
RedisDB: 0,
|
||||
})
|
||||
|
||||
config = &CacheConfig{
|
||||
Logger: libpack_logger.New(),
|
||||
Client: mockedCache,
|
||||
TTL: 5,
|
||||
}
|
||||
|
||||
hash := "00000000000000000000000000000000001337"
|
||||
data := []byte("it's fine.")
|
||||
|
||||
b.SetParallelism(Parallelism)
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
CacheStore(hash, data)
|
||||
}
|
||||
})
|
||||
}
|
||||
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
package libpack_cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
assertions "github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type Tests struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
var (
|
||||
assert *assertions.Assertions
|
||||
redisMockServer, _ = miniredis.Run()
|
||||
)
|
||||
|
||||
func (suite *Tests) BeforeTest(suiteName, testName string) {
|
||||
}
|
||||
|
||||
func (suite *Tests) SetupTest() {
|
||||
cacheStats = &CacheStats{}
|
||||
assert = assertions.New(suite.T())
|
||||
}
|
||||
|
||||
// TearDownTest is run after each test to clean up
|
||||
func (suite *Tests) TearDownTest() {
|
||||
}
|
||||
|
||||
func TestSuite(t *testing.T) {
|
||||
suite.Run(t, new(Tests))
|
||||
}
|
||||
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
package libpack_cache
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
func (suite *Tests) Test_cacheLookupInmemory() {
|
||||
config = &CacheConfig{
|
||||
Logger: libpack_logger.New(),
|
||||
Client: libpack_cache_memory.New(5 * time.Minute),
|
||||
TTL: 5,
|
||||
}
|
||||
|
||||
type args struct {
|
||||
hash string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []byte
|
||||
addCache struct {
|
||||
data []byte
|
||||
}
|
||||
}{
|
||||
{
|
||||
name: "test_non_existent",
|
||||
args: args{
|
||||
hash: "00000000000000000000000000000000000000",
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "test_existent",
|
||||
args: args{
|
||||
hash: "00000000000000000000000000000000001337",
|
||||
},
|
||||
want: []byte("it's fine."),
|
||||
addCache: struct {
|
||||
data []byte
|
||||
}{
|
||||
data: []byte("it's fine."),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
if tt.addCache.data != nil {
|
||||
CacheStore(tt.args.hash, tt.addCache.data)
|
||||
}
|
||||
got := CacheLookup(tt.args.hash)
|
||||
assert.Equal(tt.want, got, "Unexpected cache lookup result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *Tests) Test_cacheLookupRedis() {
|
||||
// redis_server := envutil.Getenv("REDIS_SERVER", "localhost:6379")
|
||||
// config.Client = libpack_cache_redis.NewClient(&libpack_cache_redis.RedisClientConfig{
|
||||
// RedisServer: redis_server,
|
||||
// RedisPassword: "",
|
||||
// RedisDB: 0,
|
||||
// })
|
||||
|
||||
mockedCache := libpack_cache_redis.New(&libpack_cache_redis.RedisClientConfig{
|
||||
RedisServer: redisMockServer.Addr(),
|
||||
RedisDB: 0,
|
||||
})
|
||||
|
||||
config = &CacheConfig{
|
||||
Logger: libpack_logger.New(),
|
||||
Client: mockedCache,
|
||||
TTL: 5,
|
||||
}
|
||||
|
||||
type args struct {
|
||||
hash string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []byte
|
||||
addCache struct {
|
||||
data []byte
|
||||
}
|
||||
}{
|
||||
{
|
||||
name: "test_non_existent",
|
||||
args: args{
|
||||
hash: "00000000000000000000000000000000000000",
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "test_existent",
|
||||
args: args{
|
||||
hash: "00000000000000000000000000000000001337",
|
||||
},
|
||||
want: []byte("it's fine."),
|
||||
addCache: struct {
|
||||
data []byte
|
||||
}{
|
||||
data: []byte("it's fine."),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
if tt.addCache.data != nil {
|
||||
CacheStore(tt.args.hash, tt.addCache.data)
|
||||
}
|
||||
got := CacheLookup(tt.args.hash)
|
||||
assert.Equal(tt.want, got, "Unexpected cache lookup result")
|
||||
})
|
||||
}
|
||||
}
|
||||
+10
-45
@@ -1,4 +1,4 @@
|
||||
package libpack_cache
|
||||
package libpack_cache_memory
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -19,7 +19,7 @@ type Cache struct {
|
||||
decompressPool sync.Pool
|
||||
entries sync.Map
|
||||
globalTTL time.Duration
|
||||
mu sync.RWMutex // Added sync.RWMutex field for locking
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func New(globalTTL time.Duration) *Cache {
|
||||
@@ -52,9 +52,6 @@ func (c *Cache) cleanupRoutine(globalTTL time.Duration) {
|
||||
}
|
||||
|
||||
func (c *Cache) Set(key string, value []byte, ttl time.Duration) {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
|
||||
expiresAt := time.Now().Add(ttl)
|
||||
|
||||
compressedValue, err := c.compress(value)
|
||||
@@ -71,9 +68,6 @@ func (c *Cache) Set(key string, value []byte, ttl time.Duration) {
|
||||
}
|
||||
|
||||
func (c *Cache) Get(key string) ([]byte, bool) {
|
||||
c.rlock()
|
||||
defer c.runlock()
|
||||
|
||||
entry, ok := c.entries.Load(key)
|
||||
if !ok || entry.(CacheEntry).ExpiresAt.Before(time.Now()) {
|
||||
return nil, false
|
||||
@@ -88,39 +82,32 @@ func (c *Cache) Get(key string) ([]byte, bool) {
|
||||
}
|
||||
|
||||
func (c *Cache) Delete(key string) {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
|
||||
c.entries.Delete(key)
|
||||
}
|
||||
|
||||
func (c *Cache) Clear() {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
|
||||
c.entries.Range(func(key, value interface{}) bool {
|
||||
c.entries.Delete(key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Cache) CountQueries() int {
|
||||
c.rlock()
|
||||
defer c.runlock()
|
||||
|
||||
func (c *Cache) CountQueries() int64 {
|
||||
var count int
|
||||
c.entries.Range(func(_, _ interface{}) bool {
|
||||
count++
|
||||
return true
|
||||
})
|
||||
return count
|
||||
return int64(count)
|
||||
}
|
||||
|
||||
func (c *Cache) compress(data []byte) ([]byte, error) {
|
||||
w := c.compressPool.Get().(*gzip.Writer)
|
||||
defer c.compressPool.Put(w)
|
||||
|
||||
var buf bytes.Buffer
|
||||
w := c.compressPool.Get().(*gzip.Writer)
|
||||
defer func() {
|
||||
w.Close()
|
||||
c.compressPool.Put(w)
|
||||
}()
|
||||
w.Reset(&buf)
|
||||
if _, err := w.Write(data); err != nil {
|
||||
return nil, err
|
||||
@@ -149,11 +136,7 @@ func (c *Cache) decompress(data []byte) ([]byte, error) {
|
||||
c.decompressPool.Put(r)
|
||||
}()
|
||||
|
||||
decompressedData, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decompressedData, nil
|
||||
return io.ReadAll(r)
|
||||
}
|
||||
|
||||
func (c *Cache) CleanExpiredEntries() {
|
||||
@@ -166,21 +149,3 @@ func (c *Cache) CleanExpiredEntries() {
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Private methods to handle locking
|
||||
|
||||
func (c *Cache) lock() {
|
||||
c.mu.Lock()
|
||||
}
|
||||
|
||||
func (c *Cache) unlock() {
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *Cache) rlock() {
|
||||
c.mu.RLock()
|
||||
}
|
||||
|
||||
func (c *Cache) runlock() {
|
||||
c.mu.RUnlock()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package libpack_cache
|
||||
package libpack_cache_memory
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
// Assume that New function initializes the cache and it is defined somewhere in the libpack_cache package.
|
||||
|
||||
func BenchmarkCacheSet(b *testing.B) {
|
||||
func BenchmarkMemCacheSet(b *testing.B) {
|
||||
cache := New(30 * time.Second) // Initializing the cache with a TTL of 30 seconds
|
||||
key := "benchmark-key"
|
||||
value := []byte("benchmark-value")
|
||||
@@ -19,7 +19,7 @@ func BenchmarkCacheSet(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCacheGet(b *testing.B) {
|
||||
func BenchmarkMemCacheGet(b *testing.B) {
|
||||
cache := New(30 * time.Second) // Initializing the cache
|
||||
key := "benchmark-key"
|
||||
value := []byte("benchmark-value")
|
||||
@@ -32,7 +32,7 @@ func BenchmarkCacheGet(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCacheExpire(b *testing.B) {
|
||||
func BenchmarkMemCacheExpire(b *testing.B) {
|
||||
key := "benchmark-expire-key"
|
||||
value := []byte("benchmark-value")
|
||||
ttl := 5 * time.Millisecond // Setting a short TTL for quick expiration
|
||||
@@ -45,7 +45,7 @@ func BenchmarkCacheExpire(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCacheStats(b *testing.B) {
|
||||
func BenchmarkMemCacheStats(b *testing.B) {
|
||||
cache := New(30 * time.Second) // Initializing the cache
|
||||
key := "benchmark-key"
|
||||
value := []byte("benchmark-value")
|
||||
@@ -1,4 +1,4 @@
|
||||
package libpack_cache
|
||||
package libpack_cache_memory
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -7,25 +7,25 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type CacheTestSuite struct {
|
||||
type MemoryTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (suite *CacheTestSuite) SetupTest() {
|
||||
func (suite *MemoryTestSuite) SetupTest() {
|
||||
}
|
||||
|
||||
func TestCachingTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(CacheTestSuite))
|
||||
suite.Run(t, new(MemoryTestSuite))
|
||||
}
|
||||
|
||||
func (suite *CacheTestSuite) Test_New() {
|
||||
func (suite *MemoryTestSuite) Test_New() {
|
||||
suite.T().Run("should return a new cache", func(t *testing.T) {
|
||||
cache := New(2 * time.Second)
|
||||
suite.NotNil(cache)
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *CacheTestSuite) Test_CacheUse() {
|
||||
func (suite *MemoryTestSuite) Test_CacheUse() {
|
||||
cache := New(30 * time.Second)
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -50,7 +50,7 @@ func (suite *CacheTestSuite) Test_CacheUse() {
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *CacheTestSuite) Test_CacheDelete() {
|
||||
func (suite *MemoryTestSuite) Test_CacheDelete() {
|
||||
cache := New(30 * time.Second)
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -79,7 +79,7 @@ func (suite *CacheTestSuite) Test_CacheDelete() {
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *CacheTestSuite) Test_CacheExpire() {
|
||||
func (suite *MemoryTestSuite) Test_CacheExpire() {
|
||||
cache := New(30 * time.Second)
|
||||
tests := []struct {
|
||||
name string
|
||||
Vendored
-77
@@ -1,77 +0,0 @@
|
||||
package libpack_redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
redis "github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var ()
|
||||
|
||||
type RedisConfig struct {
|
||||
client *redis.Client
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func prependKeyName(key string) string {
|
||||
return "gmp_cache:" + key
|
||||
}
|
||||
|
||||
type RedisClientConfig struct {
|
||||
RedisServer string
|
||||
RedisPassword string
|
||||
RedisDB int
|
||||
}
|
||||
|
||||
func NewClient(redisClientConfig *RedisClientConfig) *RedisConfig {
|
||||
c := &RedisConfig{
|
||||
client: redis.NewClient(&redis.Options{
|
||||
Addr: redisClientConfig.RedisServer,
|
||||
Password: redisClientConfig.RedisPassword,
|
||||
DB: redisClientConfig.RedisDB,
|
||||
}),
|
||||
ctx: context.Background(),
|
||||
}
|
||||
_, err := c.client.Ping(c.ctx).Result()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Set(key string, value []byte, ttl time.Duration) {
|
||||
c.client.Set(c.ctx, prependKeyName(key), value, ttl)
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Get(key string) ([]byte, bool) {
|
||||
val, err := c.client.Get(c.ctx, prependKeyName(key)).Result()
|
||||
if err == redis.Nil || err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return []byte(val), true
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Delete(key string) {
|
||||
c.client.Del(c.ctx, prependKeyName(key))
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Clear() {
|
||||
c.client.FlushDB(c.ctx)
|
||||
}
|
||||
|
||||
func (c *RedisConfig) CountQueries() int {
|
||||
keys, err := c.client.Keys(c.ctx, prependKeyName("*")).Result()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return len(keys)
|
||||
}
|
||||
|
||||
func (c *RedisConfig) CountQueriesWithPattern(pattern string) int {
|
||||
keys, err := c.client.Keys(c.ctx, prependKeyName(pattern)).Result()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return len(keys)
|
||||
}
|
||||
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
package libpack_cache_redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"sync"
|
||||
|
||||
redis "github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type RedisConfig struct {
|
||||
client *redis.Client
|
||||
ctx context.Context
|
||||
prefix string
|
||||
builderPool *sync.Pool
|
||||
}
|
||||
|
||||
func (c *RedisConfig) prependKeyName(key string) string {
|
||||
builder := c.builderPool.Get().(*strings.Builder)
|
||||
defer c.builderPool.Put(builder)
|
||||
builder.Reset()
|
||||
builder.WriteString(c.prefix)
|
||||
builder.WriteString(key)
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
type RedisClientConfig struct {
|
||||
RedisServer string
|
||||
RedisPassword string
|
||||
RedisDB int
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func New(redisClientConfig *RedisClientConfig) *RedisConfig {
|
||||
c := &RedisConfig{
|
||||
client: redis.NewClient(&redis.Options{
|
||||
Addr: redisClientConfig.RedisServer,
|
||||
Password: redisClientConfig.RedisPassword,
|
||||
DB: redisClientConfig.RedisDB,
|
||||
}),
|
||||
ctx: context.Background(),
|
||||
prefix: redisClientConfig.Prefix,
|
||||
builderPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &strings.Builder{}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := c.client.Ping(c.ctx).Result()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Set(key string, value []byte, ttl time.Duration) {
|
||||
c.client.Set(c.ctx, c.prependKeyName(key), value, ttl)
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Get(key string) ([]byte, bool) {
|
||||
val, err := c.client.Get(c.ctx, c.prependKeyName(key)).Result()
|
||||
if err == redis.Nil {
|
||||
return nil, false
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return []byte(val), true
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Delete(key string) {
|
||||
c.client.Del(c.ctx, c.prependKeyName(key))
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Clear() {
|
||||
c.client.FlushDB(c.ctx)
|
||||
}
|
||||
|
||||
func (c *RedisConfig) CountQueries() int64 {
|
||||
keys, err := c.client.Keys(c.ctx, c.prependKeyName("*")).Result()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return int64(len(keys))
|
||||
}
|
||||
|
||||
func (c *RedisConfig) CountQueriesWithPattern(pattern string) int {
|
||||
keys, err := c.client.Keys(c.ctx, c.prependKeyName(pattern)).Result()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return len(keys)
|
||||
}
|
||||
+22
-17
@@ -1,23 +1,24 @@
|
||||
package libpack_redis
|
||||
package libpack_cache_redis
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gookit/goutil/envutil"
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type RedisConfigSuite struct {
|
||||
suite.Suite
|
||||
redisConfig *RedisConfig
|
||||
redisConfig *RedisConfig
|
||||
redis_server *miniredis.Miniredis
|
||||
}
|
||||
|
||||
func (suite *RedisConfigSuite) SetupTest() {
|
||||
redis_server := envutil.Getenv("REDIS_SERVER", "localhost:6379")
|
||||
suite.redisConfig = NewClient(&RedisClientConfig{
|
||||
RedisServer: redis_server,
|
||||
suite.redis_server, _ = miniredis.Run()
|
||||
suite.redisConfig = New(&RedisClientConfig{
|
||||
RedisServer: suite.redis_server.Addr(),
|
||||
RedisPassword: "",
|
||||
RedisDB: 0,
|
||||
})
|
||||
@@ -29,7 +30,7 @@ func TestRedisConfigSuite(t *testing.T) {
|
||||
}
|
||||
|
||||
func (suite *RedisConfigSuite) TestSet() {
|
||||
key := "testkey"
|
||||
key := "testkeyset"
|
||||
value := []byte("testvalue")
|
||||
suite.redisConfig.Delete(key) // Ensure the key is deleted before the test
|
||||
|
||||
@@ -50,9 +51,9 @@ func (suite *RedisConfigSuite) TestSet() {
|
||||
}
|
||||
|
||||
func (suite *RedisConfigSuite) TestSetWithExpiry() {
|
||||
key := "testkey"
|
||||
value := []byte("testvalue")
|
||||
expiry := 1 * time.Second
|
||||
key := "testkey_with_expiry"
|
||||
value := []byte("testvaluewithexpiry")
|
||||
expiry := 2 * time.Second
|
||||
suite.redisConfig.Delete(key) // Ensure the key is deleted before the test
|
||||
|
||||
// Test writing a new key-value pair
|
||||
@@ -60,17 +61,19 @@ func (suite *RedisConfigSuite) TestSetWithExpiry() {
|
||||
storedValue, found := suite.redisConfig.Get(key)
|
||||
assert.True(suite.T(), found)
|
||||
assert.Equal(suite.T(), value, storedValue)
|
||||
_, found = suite.redisConfig.Get(key)
|
||||
assert.True(suite.T(), found, "Key should exist")
|
||||
|
||||
// Test that key expires after the specified time
|
||||
time.Sleep(2 * time.Second)
|
||||
suite.redis_server.FastForward(3 * time.Second)
|
||||
_, found = suite.redisConfig.Get(key)
|
||||
assert.False(suite.T(), found)
|
||||
assert.False(suite.T(), found, "Key should have expired after 2 seconds")
|
||||
|
||||
suite.redisConfig.Delete(key) // Clean up after the test
|
||||
}
|
||||
|
||||
func (suite *RedisConfigSuite) TestGet() {
|
||||
key := "testkey"
|
||||
key := "testkeyget"
|
||||
value := []byte("testvalue")
|
||||
suite.redisConfig.Set(key, value, 0) // Set the key-value pair
|
||||
storedValue, found := suite.redisConfig.Get(key)
|
||||
@@ -79,7 +82,7 @@ func (suite *RedisConfigSuite) TestGet() {
|
||||
}
|
||||
|
||||
func (suite *RedisConfigSuite) TestDeleteKey() {
|
||||
key := "testkey"
|
||||
key := "testkeydelete"
|
||||
value := []byte("testvalue")
|
||||
suite.redisConfig.Set(key, value, 0) // Set the key-value pair
|
||||
suite.redisConfig.Delete(key)
|
||||
@@ -89,7 +92,7 @@ func (suite *RedisConfigSuite) TestDeleteKey() {
|
||||
|
||||
func (suite *RedisConfigSuite) TestCheckIfKeyExists() {
|
||||
ttl := time.Duration(10) * time.Second
|
||||
key := "testkey"
|
||||
key := "testkeyifexists"
|
||||
value := []byte("testvalue")
|
||||
suite.redisConfig.Set(key, value, ttl) // Set the key-value pair
|
||||
_, found := suite.redisConfig.Get(key)
|
||||
@@ -106,8 +109,8 @@ func (suite *RedisConfigSuite) TestGetKeys() {
|
||||
suite.redisConfig.Set("testkey2", []byte("testvalue2"), ttl)
|
||||
suite.redisConfig.Set("otherkey", []byte("othervalue"), ttl)
|
||||
|
||||
keys, _ := suite.redisConfig.client.Keys(suite.redisConfig.ctx, prependKeyName("testkey*")).Result()
|
||||
expectedKeys := []string{prependKeyName("testkey1"), prependKeyName("testkey2")}
|
||||
keys, _ := suite.redisConfig.client.Keys(suite.redisConfig.ctx, "testkey*").Result()
|
||||
expectedKeys := []string{"testkey1", "testkey2"}
|
||||
assert.ElementsMatch(suite.T(), expectedKeys, keys)
|
||||
|
||||
suite.redisConfig.client.Del(suite.redisConfig.ctx, "testkey1", "testkey2", "otherkey")
|
||||
@@ -120,6 +123,8 @@ func (suite *RedisConfigSuite) TestGetKeysCount() {
|
||||
suite.redisConfig.Set("otherkey", []byte("othervalue"), ttl)
|
||||
|
||||
assert.Equal(suite.T(), 2, suite.redisConfig.CountQueriesWithPattern("testkey*"))
|
||||
assert.Equal(suite.T(), 1, suite.redisConfig.CountQueriesWithPattern("otherkey*"))
|
||||
assert.Equal(suite.T(), int64(3), suite.redisConfig.CountQueries())
|
||||
|
||||
suite.redisConfig.client.Del(suite.redisConfig.ctx, "testkey1", "testkey2", "otherkey")
|
||||
}
|
||||
Reference in New Issue
Block a user