diff --git a/cache/redis/redis.go b/cache/redis/redis.go index 9fcc24a..a4f27b1 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -83,7 +83,15 @@ type RedisClientConfig struct { // entry. const defaultTTL = 60 * time.Second +// DefaultKeyPrefix namespaces cache keys when no prefix is configured, so Clear +// and CountQueries never touch keys of other applications on a shared Redis. +const DefaultKeyPrefix = "gmp:" + func New(redisClientConfig *RedisClientConfig) (*RedisConfig, error) { + prefix := redisClientConfig.Prefix + if prefix == "" { + prefix = DefaultKeyPrefix + } c := &RedisConfig{ client: redis.NewClient(&redis.Options{ Addr: redisClientConfig.RedisServer, @@ -91,7 +99,7 @@ func New(redisClientConfig *RedisClientConfig) (*RedisConfig, error) { DB: redisClientConfig.RedisDB, }), ctx: context.Background(), - prefix: redisClientConfig.Prefix, + prefix: prefix, builderPool: &sync.Pool{ New: func() any { return &strings.Builder{} diff --git a/cache/redis/redis_default_prefix_test.go b/cache/redis/redis_default_prefix_test.go new file mode 100644 index 0000000..4b82eda --- /dev/null +++ b/cache/redis/redis_default_prefix_test.go @@ -0,0 +1,30 @@ +package libpack_cache_redis + +import ( + "testing" + + "github.com/alicebob/miniredis/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Pins theorems/Theorems/RedisClear.lean: with no configured prefix, Clear must still spare foreign keys. +func TestClear_DefaultPrefixSparesForeignKeys(t *testing.T) { + t.Parallel() + s, err := miniredis.Run() + require.NoError(t, err) + t.Cleanup(s.Close) + + require.NoError(t, s.Set("other-app:session", "keep")) + + c, err := New(&RedisClientConfig{RedisServer: s.Addr()}) + require.NoError(t, err) + require.NoError(t, c.Set("q1", []byte("v"), 0)) + + require.NoError(t, c.Clear()) + + assert.True(t, s.Exists("other-app:session"), "foreign key was deleted") + _, found, err := c.Get("q1") + require.NoError(t, err) + assert.False(t, found, "own key must be cleared") +} diff --git a/cache/redis/redis_test.go b/cache/redis/redis_test.go index 59b5c81..95c8f45 100644 --- a/cache/redis/redis_test.go +++ b/cache/redis/redis_test.go @@ -131,11 +131,11 @@ func (suite *RedisConfigSuite) TestGetKeys() { 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"} + keys, _ := suite.redisConfig.client.Keys(suite.redisConfig.ctx, DefaultKeyPrefix+"testkey*").Result() + expectedKeys := []string{DefaultKeyPrefix + "testkey1", DefaultKeyPrefix + "testkey2"} assert.ElementsMatch(suite.T(), expectedKeys, keys) - suite.redisConfig.client.Del(suite.redisConfig.ctx, "testkey1", "testkey2", "otherkey") + suite.redisConfig.client.Del(suite.redisConfig.ctx, DefaultKeyPrefix+"testkey1", DefaultKeyPrefix+"testkey2", DefaultKeyPrefix+"otherkey") } func (suite *RedisConfigSuite) TestGetKeysCount() { @@ -154,5 +154,5 @@ func (suite *RedisConfigSuite) TestGetKeysCount() { assert.NoError(suite.T(), err) assert.Equal(suite.T(), int64(3), count3) - suite.redisConfig.client.Del(suite.redisConfig.ctx, "testkey1", "testkey2", "otherkey") + suite.redisConfig.client.Del(suite.redisConfig.ctx, DefaultKeyPrefix+"testkey1", DefaultKeyPrefix+"testkey2", DefaultKeyPrefix+"otherkey") }