mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-19 06:27:38 +00:00
improvements mid may 2025 (#24)
* General improvements and bug fixes. * Improve tests coverage. * fixup! Improve tests coverage. * Update README.md with latest changes. * Fix the uint32 * Resolve issue with race condition for logging. * fixup! Merge remote-tracking branch 'origin/main' into improvements-mid-apr-2025 * Fix the test of the rate limiter * Add default ratelimit.json file * Update dependencies. * Significant refactor. * fixup! Significant refactor. * fixup! Merge remote-tracking branch 'origin/main' into improvements-mid-apr-2025 * fixup! fixup! Merge remote-tracking branch 'origin/main' into improvements-mid-apr-2025 * fixup! fixup! fixup! Merge remote-tracking branch 'origin/main' into improvements-mid-apr-2025 * fixup! fixup! fixup! fixup! fixup! Merge remote-tracking branch 'origin/main' into improvements-mid-apr-2025 * fixup! fixup! fixup! fixup! fixup! fixup! Merge remote-tracking branch 'origin/main' into improvements-mid-apr-2025 * fixup! fixup! fixup! fixup! fixup! fixup! fixup! Merge remote-tracking branch 'origin/main' into improvements-mid-apr-2025 * fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Merge remote-tracking branch 'origin/main' into improvements-mid-apr-2025 * fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Merge remote-tracking branch 'origin/main' into improvements-mid-apr-2025 * fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Merge remote-tracking branch 'origin/main' into improvements-mid-apr-2025
This commit is contained in:
Vendored
+45
-20
@@ -3,9 +3,8 @@ package libpack_cache_redis
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
redis "github.com/redis/go-redis/v9"
|
||||
)
|
||||
@@ -33,7 +32,7 @@ type RedisClientConfig struct {
|
||||
RedisDB int
|
||||
}
|
||||
|
||||
func New(redisClientConfig *RedisClientConfig) *RedisConfig {
|
||||
func New(redisClientConfig *RedisClientConfig) (*RedisConfig, error) {
|
||||
c := &RedisConfig{
|
||||
client: redis.NewClient(&redis.Options{
|
||||
Addr: redisClientConfig.RedisServer,
|
||||
@@ -51,46 +50,72 @@ func New(redisClientConfig *RedisClientConfig) *RedisConfig {
|
||||
|
||||
_, err := c.client.Ping(c.ctx).Result()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
return c
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Set(key string, value []byte, ttl time.Duration) {
|
||||
c.client.Set(c.ctx, c.prependKeyName(key), value, ttl)
|
||||
func (c *RedisConfig) Set(key string, value []byte, ttl time.Duration) error {
|
||||
return c.client.Set(c.ctx, c.prependKeyName(key), value, ttl).Err()
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Get(key string) ([]byte, bool) {
|
||||
func (c *RedisConfig) Get(key string) ([]byte, bool, error) {
|
||||
val, err := c.client.Get(c.ctx, c.prependKeyName(key)).Result()
|
||||
if err == redis.Nil {
|
||||
return nil, false
|
||||
return nil, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false
|
||||
return nil, false, err
|
||||
}
|
||||
return []byte(val), true
|
||||
return []byte(val), true, nil
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Delete(key string) {
|
||||
c.client.Del(c.ctx, c.prependKeyName(key))
|
||||
func (c *RedisConfig) Delete(key string) error {
|
||||
return c.client.Del(c.ctx, c.prependKeyName(key)).Err()
|
||||
}
|
||||
|
||||
func (c *RedisConfig) Clear() {
|
||||
c.client.FlushDB(c.ctx)
|
||||
func (c *RedisConfig) Clear() error {
|
||||
return c.client.FlushDB(c.ctx).Err()
|
||||
}
|
||||
|
||||
func (c *RedisConfig) CountQueries() int64 {
|
||||
func (c *RedisConfig) CountQueries() (int64, error) {
|
||||
keys, err := c.client.Keys(c.ctx, c.prependKeyName("*")).Result()
|
||||
if err != nil {
|
||||
return 0
|
||||
return 0, err
|
||||
}
|
||||
return int64(len(keys))
|
||||
return int64(len(keys)), nil
|
||||
}
|
||||
|
||||
func (c *RedisConfig) CountQueriesWithPattern(pattern string) int {
|
||||
func (c *RedisConfig) CountQueriesWithPattern(pattern string) (int, error) {
|
||||
keys, err := c.client.Keys(c.ctx, c.prependKeyName(pattern)).Result()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(keys), nil
|
||||
}
|
||||
|
||||
// GetMemoryUsage returns an approximation of memory usage for Redis
|
||||
// For Redis, this is not as accurate as the memory cache implementation
|
||||
// as actual memory is managed by Redis server
|
||||
func (c *RedisConfig) GetMemoryUsage() int64 {
|
||||
// We could attempt to get memory usage from Redis info
|
||||
// but for now, we'll just return 0 since Redis manages its own memory
|
||||
// and this information would require parsing the INFO command output
|
||||
_, err := c.client.Info(c.ctx, "memory").Result()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return len(keys)
|
||||
|
||||
// Just return 0 as a placeholder since Redis manages its own memory
|
||||
// In a production environment, you could parse the Redis INFO command result
|
||||
// to extract actual "used_memory" value
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetMaxMemorySize returns the configured max memory for Redis
|
||||
// In Redis, this would be the 'maxmemory' configuration value
|
||||
func (c *RedisConfig) GetMaxMemorySize() int64 {
|
||||
// Return a default value as Redis manages its own memory limits
|
||||
// In a production environment, you could get this from Redis config
|
||||
return 0
|
||||
}
|
||||
|
||||
Vendored
+22
-10
@@ -17,34 +17,46 @@ func TestRedisClear(t *testing.T) {
|
||||
defer s.Close()
|
||||
|
||||
// Create a Redis client
|
||||
redisConfig := New(&RedisClientConfig{
|
||||
redisConfig, err := New(&RedisClientConfig{
|
||||
RedisServer: s.Addr(),
|
||||
RedisPassword: "",
|
||||
RedisDB: 0,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create Redis client: %v", err)
|
||||
}
|
||||
|
||||
// Add some test data
|
||||
ttl := time.Duration(60) * time.Second
|
||||
redisConfig.Set("key1", []byte("value1"), ttl)
|
||||
redisConfig.Set("key2", []byte("value2"), ttl)
|
||||
redisConfig.Set("key3", []byte("value3"), ttl)
|
||||
err = redisConfig.Set("key1", []byte("value1"), ttl)
|
||||
assert.NoError(t, err)
|
||||
err = redisConfig.Set("key2", []byte("value2"), ttl)
|
||||
assert.NoError(t, err)
|
||||
err = redisConfig.Set("key3", []byte("value3"), ttl)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify keys exist
|
||||
count := redisConfig.CountQueries()
|
||||
count, err := redisConfig.CountQueries()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(3), count, "Expected 3 keys before clearing cache")
|
||||
|
||||
// Clear the cache
|
||||
redisConfig.Clear()
|
||||
err = redisConfig.Clear()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify all keys are gone
|
||||
count = redisConfig.CountQueries()
|
||||
count, err = redisConfig.CountQueries()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(0), count, "Expected 0 keys after clearing cache")
|
||||
|
||||
// Verify individual keys are gone
|
||||
_, found := redisConfig.Get("key1")
|
||||
_, found, err := redisConfig.Get("key1")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, found, "Key1 should be deleted after Clear")
|
||||
_, found = redisConfig.Get("key2")
|
||||
_, found, err = redisConfig.Get("key2")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, found, "Key2 should be deleted after Clear")
|
||||
_, found = redisConfig.Get("key3")
|
||||
_, found, err = redisConfig.Get("key3")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, found, "Key3 should be deleted after Clear")
|
||||
}
|
||||
|
||||
Vendored
+52
-24
@@ -17,11 +17,13 @@ type RedisConfigSuite struct {
|
||||
|
||||
func (suite *RedisConfigSuite) SetupTest() {
|
||||
suite.redis_server, _ = miniredis.Run()
|
||||
suite.redisConfig = New(&RedisClientConfig{
|
||||
var err error
|
||||
suite.redisConfig, err = New(&RedisClientConfig{
|
||||
RedisServer: suite.redis_server.Addr(),
|
||||
RedisPassword: "",
|
||||
RedisDB: 0,
|
||||
})
|
||||
assert.NoError(suite.T(), err)
|
||||
suite.redisConfig.Delete("testkey")
|
||||
}
|
||||
|
||||
@@ -35,15 +37,19 @@ func (suite *RedisConfigSuite) TestSet() {
|
||||
suite.redisConfig.Delete(key) // Ensure the key is deleted before the test
|
||||
|
||||
// Test writing a new key-value pair
|
||||
suite.redisConfig.Set(key, value, 0)
|
||||
storedValue, found := suite.redisConfig.Get(key)
|
||||
err := suite.redisConfig.Set(key, value, 0)
|
||||
assert.NoError(suite.T(), err)
|
||||
storedValue, found, err := suite.redisConfig.Get(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.True(suite.T(), found)
|
||||
assert.Equal(suite.T(), value, storedValue)
|
||||
|
||||
// Test overwriting an existing key-value pair
|
||||
newValue := []byte("newvalue")
|
||||
suite.redisConfig.Set(key, newValue, 0)
|
||||
storedValue, found = suite.redisConfig.Get(key)
|
||||
err = suite.redisConfig.Set(key, newValue, 0)
|
||||
assert.NoError(suite.T(), err)
|
||||
storedValue, found, err = suite.redisConfig.Get(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.True(suite.T(), found)
|
||||
assert.Equal(suite.T(), newValue, storedValue)
|
||||
|
||||
@@ -57,16 +63,20 @@ func (suite *RedisConfigSuite) TestSetWithExpiry() {
|
||||
suite.redisConfig.Delete(key) // Ensure the key is deleted before the test
|
||||
|
||||
// Test writing a new key-value pair
|
||||
suite.redisConfig.Set(key, value, expiry)
|
||||
storedValue, found := suite.redisConfig.Get(key)
|
||||
err := suite.redisConfig.Set(key, value, expiry)
|
||||
assert.NoError(suite.T(), err)
|
||||
storedValue, found, err := suite.redisConfig.Get(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.True(suite.T(), found)
|
||||
assert.Equal(suite.T(), value, storedValue)
|
||||
_, found = suite.redisConfig.Get(key)
|
||||
_, found, err = suite.redisConfig.Get(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.True(suite.T(), found, "Key should exist")
|
||||
|
||||
// Test that key expires after the specified time
|
||||
suite.redis_server.FastForward(3 * time.Second)
|
||||
_, found = suite.redisConfig.Get(key)
|
||||
_, found, err = suite.redisConfig.Get(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.False(suite.T(), found, "Key should have expired after 2 seconds")
|
||||
|
||||
suite.redisConfig.Delete(key) // Clean up after the test
|
||||
@@ -75,8 +85,10 @@ func (suite *RedisConfigSuite) TestSetWithExpiry() {
|
||||
func (suite *RedisConfigSuite) TestGet() {
|
||||
key := "testkeyget"
|
||||
value := []byte("testvalue")
|
||||
suite.redisConfig.Set(key, value, 0) // Set the key-value pair
|
||||
storedValue, found := suite.redisConfig.Get(key)
|
||||
err := suite.redisConfig.Set(key, value, 0) // Set the key-value pair
|
||||
assert.NoError(suite.T(), err)
|
||||
storedValue, found, err := suite.redisConfig.Get(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.True(suite.T(), found)
|
||||
assert.Equal(suite.T(), value, storedValue)
|
||||
}
|
||||
@@ -84,9 +96,12 @@ func (suite *RedisConfigSuite) TestGet() {
|
||||
func (suite *RedisConfigSuite) TestDeleteKey() {
|
||||
key := "testkeydelete"
|
||||
value := []byte("testvalue")
|
||||
suite.redisConfig.Set(key, value, 0) // Set the key-value pair
|
||||
suite.redisConfig.Delete(key)
|
||||
_, found := suite.redisConfig.Get(key)
|
||||
err := suite.redisConfig.Set(key, value, 0) // Set the key-value pair
|
||||
assert.NoError(suite.T(), err)
|
||||
err = suite.redisConfig.Delete(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
_, found, err := suite.redisConfig.Get(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.False(suite.T(), found)
|
||||
}
|
||||
|
||||
@@ -94,20 +109,27 @@ func (suite *RedisConfigSuite) TestCheckIfKeyExists() {
|
||||
ttl := time.Duration(10) * time.Second
|
||||
key := "testkeyifexists"
|
||||
value := []byte("testvalue")
|
||||
suite.redisConfig.Set(key, value, ttl) // Set the key-value pair
|
||||
_, found := suite.redisConfig.Get(key)
|
||||
err := suite.redisConfig.Set(key, value, ttl) // Set the key-value pair
|
||||
assert.NoError(suite.T(), err)
|
||||
_, found, err := suite.redisConfig.Get(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.True(suite.T(), found)
|
||||
|
||||
suite.redisConfig.Delete(key)
|
||||
_, found = suite.redisConfig.Get(key)
|
||||
err = suite.redisConfig.Delete(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
_, found, err = suite.redisConfig.Get(key)
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.False(suite.T(), found)
|
||||
}
|
||||
|
||||
func (suite *RedisConfigSuite) TestGetKeys() {
|
||||
ttl := time.Duration(10) * time.Second
|
||||
suite.redisConfig.Set("testkey1", []byte("testvalue1"), ttl)
|
||||
suite.redisConfig.Set("testkey2", []byte("testvalue2"), ttl)
|
||||
suite.redisConfig.Set("otherkey", []byte("othervalue"), ttl)
|
||||
err := suite.redisConfig.Set("testkey1", []byte("testvalue1"), ttl)
|
||||
assert.NoError(suite.T(), err)
|
||||
err = suite.redisConfig.Set("testkey2", []byte("testvalue2"), ttl)
|
||||
assert.NoError(suite.T(), err)
|
||||
err = suite.redisConfig.Set("otherkey", []byte("othervalue"), ttl)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
keys, _ := suite.redisConfig.client.Keys(suite.redisConfig.ctx, "testkey*").Result()
|
||||
expectedKeys := []string{"testkey1", "testkey2"}
|
||||
@@ -122,9 +144,15 @@ func (suite *RedisConfigSuite) TestGetKeysCount() {
|
||||
suite.redisConfig.Set("testkey2", []byte("testvalue2"), ttl)
|
||||
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())
|
||||
count1, err := suite.redisConfig.CountQueriesWithPattern("testkey*")
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.Equal(suite.T(), 2, count1)
|
||||
count2, err := suite.redisConfig.CountQueriesWithPattern("otherkey*")
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.Equal(suite.T(), 1, count2)
|
||||
count3, err := suite.redisConfig.CountQueries()
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.Equal(suite.T(), int64(3), count3)
|
||||
|
||||
suite.redisConfig.client.Del(suite.redisConfig.ctx, "testkey1", "testkey2", "otherkey")
|
||||
}
|
||||
|
||||
Vendored
+104
@@ -0,0 +1,104 @@
|
||||
package libpack_cache_redis
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging"
|
||||
)
|
||||
|
||||
// CacheWrapper wraps RedisConfig to implement the CacheClient interface
|
||||
// without returning errors, for backward compatibility
|
||||
type CacheWrapper struct {
|
||||
redis *RedisConfig
|
||||
logger *libpack_logger.Logger
|
||||
}
|
||||
|
||||
// NewCacheWrapper creates a new cache wrapper
|
||||
func NewCacheWrapper(config *RedisConfig, logger *libpack_logger.Logger) *CacheWrapper {
|
||||
if logger == nil {
|
||||
logger = &libpack_logger.Logger{}
|
||||
}
|
||||
return &CacheWrapper{
|
||||
redis: config,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Set stores a value with the given TTL
|
||||
func (w *CacheWrapper) Set(key string, value []byte, ttl time.Duration) {
|
||||
if err := w.redis.Set(key, value, ttl); err != nil {
|
||||
w.logger.Error(&libpack_logger.LogMessage{
|
||||
Message: "Redis set error",
|
||||
Pairs: map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"key": key,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a value
|
||||
func (w *CacheWrapper) Get(key string) ([]byte, bool) {
|
||||
value, found, err := w.redis.Get(key)
|
||||
if err != nil {
|
||||
w.logger.Error(&libpack_logger.LogMessage{
|
||||
Message: "Redis get error",
|
||||
Pairs: map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"key": key,
|
||||
},
|
||||
})
|
||||
return nil, false
|
||||
}
|
||||
return value, found
|
||||
}
|
||||
|
||||
// Delete removes a key
|
||||
func (w *CacheWrapper) Delete(key string) {
|
||||
if err := w.redis.Delete(key); err != nil {
|
||||
w.logger.Error(&libpack_logger.LogMessage{
|
||||
Message: "Redis delete error",
|
||||
Pairs: map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"key": key,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Clear removes all keys
|
||||
func (w *CacheWrapper) Clear() {
|
||||
if err := w.redis.Clear(); err != nil {
|
||||
w.logger.Error(&libpack_logger.LogMessage{
|
||||
Message: "Redis clear error",
|
||||
Pairs: map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// CountQueries returns the number of queries
|
||||
func (w *CacheWrapper) CountQueries() int64 {
|
||||
count, err := w.redis.CountQueries()
|
||||
if err != nil {
|
||||
w.logger.Error(&libpack_logger.LogMessage{
|
||||
Message: "Redis count queries error",
|
||||
Pairs: map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
},
|
||||
})
|
||||
return 0
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// GetMemoryUsage returns 0 for Redis (not applicable)
|
||||
func (w *CacheWrapper) GetMemoryUsage() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetMaxMemorySize returns 0 for Redis (not applicable)
|
||||
func (w *CacheWrapper) GetMaxMemorySize() int64 {
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user