package cache import ( "bytes" "compress/gzip" "context" "io" "time" "github.com/redis/go-redis/v9" ) // Redis implementation of the cache client. type Redis struct { client redis.UniversalClient } // NewRedis instantiate a new Redis Cache Client. func NewRedis(client redis.UniversalClient) *Redis { return &Redis{client} } // CheckStatus checks that the cache is ready, or returns an error. func (c *Redis) CheckStatus(ctx context.Context) (time.Duration, error) { before := time.Now() if err := c.client.Ping(ctx).Err(); err != nil { return 0, err } return time.Since(before), nil } // Get fetch the cached asset at the given key, and returns true only if the // asset was found. func (c *Redis) Get(key string) ([]byte, bool) { cmd := c.client.Get(context.TODO(), key) b, err := cmd.Bytes() if err != nil { return nil, false } return b, true } // MultiGet can be used to fetch several keys at once. func (c *Redis) MultiGet(keys []string) [][]byte { results := make([][]byte, len(keys)) cmd := c.client.MGet(context.TODO(), keys...) for i, val := range cmd.Val() { if buf, ok := val.(string); ok { results[i] = []byte(buf) } } return results } // Keys returns the list of keys with the given prefix. // // Note: it can be slow and should be used carefully. func (c *Redis) Keys(prefix string) []string { cmd := c.client.Keys(context.TODO(), prefix+"*") return cmd.Val() } // Clear removes a key from the cache func (c *Redis) Clear(key string) { c.client.Del(context.TODO(), key) } // Set stores an asset to the given key. func (c *Redis) Set(key string, data []byte, expiration time.Duration) { c.client.Set(context.TODO(), key, data, expiration) } // SetNX stores the data in the cache only if the key doesn't exist yet. func (c *Redis) SetNX(key string, data []byte, expiration time.Duration) { c.client.SetNX(context.TODO(), key, data, expiration) } // GetCompressed works like Get but expect a compressed asset that is // uncompressed. func (c *Redis) GetCompressed(key string) (io.Reader, bool) { r, ok := c.Get(key) if !ok { return nil, false } gr, err := gzip.NewReader(bytes.NewReader(r)) if err != nil { return nil, false } return gr, true } // SetCompressed works like Set but compress the asset data before storing it. func (c *Redis) SetCompressed(key string, data []byte, expiration time.Duration) { dataCompressed := new(bytes.Buffer) gw := gzip.NewWriter(dataCompressed) defer gw.Close() if _, err := io.Copy(gw, bytes.NewReader(data)); err != nil { return } c.Set(key, dataCompressed.Bytes(), expiration) } // RefreshTTL can be used to update the TTL of an existing entry in the cache. func (c *Redis) RefreshTTL(key string, expiration time.Duration) { c.client.Expire(context.TODO(), key, expiration) }