package cache import ( "context" "errors" "fmt" "time" goredis "github.com/redis/go-redis/v9" ) type redisCache struct { client *goredis.Client deleteScanCount int64 } func newRedisCache(client *goredis.Client, deleteScanCount int64) Cache { return &redisCache{client: client, deleteScanCount: deleteScanCount} } func (c *redisCache) Get(ctx context.Context, key string) ([]byte, error) { val, err := c.client.Get(ctx, key).Bytes() if errors.Is(err, goredis.Nil) { return nil, ErrNotFound } return val, err } func (c *redisCache) GetMulti(ctx context.Context, keys []string) (map[string][]byte, error) { if len(keys) == 0 { return map[string][]byte{}, nil } values, err := c.client.MGet(ctx, keys...).Result() if err != nil { return nil, err } result := make(map[string][]byte, len(keys)) for idx, value := range values { if value == nil { continue } switch typed := value.(type) { case string: result[keys[idx]] = []byte(typed) case []byte: dst := make([]byte, len(typed)) copy(dst, typed) result[keys[idx]] = dst default: result[keys[idx]] = []byte(fmt.Sprint(value)) } } return result, nil } func (c *redisCache) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error { return c.client.Set(ctx, key, value, ttl).Err() } func (c *redisCache) SetMulti(ctx context.Context, items []Item) error { if len(items) == 0 { return nil } pipe := c.client.Pipeline() for _, item := range items { pipe.Set(ctx, item.Key, item.Value, item.TTL) } _, err := pipe.Exec(ctx) return err } func (c *redisCache) Delete(ctx context.Context, key string) error { return c.client.Del(ctx, key).Err() } func (c *redisCache) DeleteMany(ctx context.Context, keys []string) error { if len(keys) == 0 { return nil } return c.client.Del(ctx, keys...).Err() } func (c *redisCache) DeletePrefix(ctx context.Context, prefix string) error { if prefix == "" { return nil } pattern := prefix + "*" scanCount := c.deleteScanCount if scanCount <= 0 { scanCount = defaultDeleteScanCount } var cursor uint64 for { keys, nextCursor, err := c.client.Scan(ctx, cursor, pattern, scanCount).Result() if err != nil { return err } if len(keys) > 0 { if err := c.DeleteMany(ctx, keys); err != nil { return err } } cursor = nextCursor if cursor == 0 { return nil } } }