github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/pkg/cache/impl_redis.go (about) 1 package cache 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "context" 7 "io" 8 "time" 9 10 "github.com/redis/go-redis/v9" 11 ) 12 13 // Redis implementation of the cache client. 14 type Redis struct { 15 client redis.UniversalClient 16 } 17 18 // NewRedis instantiate a new Redis Cache Client. 19 func NewRedis(client redis.UniversalClient) *Redis { 20 return &Redis{client} 21 } 22 23 // CheckStatus checks that the cache is ready, or returns an error. 24 func (c *Redis) CheckStatus(ctx context.Context) (time.Duration, error) { 25 before := time.Now() 26 if err := c.client.Ping(ctx).Err(); err != nil { 27 return 0, err 28 } 29 return time.Since(before), nil 30 } 31 32 // Get fetch the cached asset at the given key, and returns true only if the 33 // asset was found. 34 func (c *Redis) Get(key string) ([]byte, bool) { 35 cmd := c.client.Get(context.TODO(), key) 36 b, err := cmd.Bytes() 37 if err != nil { 38 return nil, false 39 } 40 41 return b, true 42 } 43 44 // MultiGet can be used to fetch several keys at once. 45 func (c *Redis) MultiGet(keys []string) [][]byte { 46 results := make([][]byte, len(keys)) 47 48 cmd := c.client.MGet(context.TODO(), keys...) 49 50 for i, val := range cmd.Val() { 51 if buf, ok := val.(string); ok { 52 results[i] = []byte(buf) 53 } 54 } 55 56 return results 57 } 58 59 // Keys returns the list of keys with the given prefix. 60 // 61 // Note: it can be slow and should be used carefully. 62 func (c *Redis) Keys(prefix string) []string { 63 cmd := c.client.Keys(context.TODO(), prefix+"*") 64 65 return cmd.Val() 66 } 67 68 // Clear removes a key from the cache 69 func (c *Redis) Clear(key string) { 70 c.client.Del(context.TODO(), key) 71 } 72 73 // Set stores an asset to the given key. 74 func (c *Redis) Set(key string, data []byte, expiration time.Duration) { 75 c.client.Set(context.TODO(), key, data, expiration) 76 } 77 78 // SetNX stores the data in the cache only if the key doesn't exist yet. 79 func (c *Redis) SetNX(key string, data []byte, expiration time.Duration) { 80 c.client.SetNX(context.TODO(), key, data, expiration) 81 } 82 83 // GetCompressed works like Get but expect a compressed asset that is 84 // uncompressed. 85 func (c *Redis) GetCompressed(key string) (io.Reader, bool) { 86 r, ok := c.Get(key) 87 if !ok { 88 return nil, false 89 } 90 91 gr, err := gzip.NewReader(bytes.NewReader(r)) 92 if err != nil { 93 return nil, false 94 } 95 96 return gr, true 97 } 98 99 // SetCompressed works like Set but compress the asset data before storing it. 100 func (c *Redis) SetCompressed(key string, data []byte, expiration time.Duration) { 101 dataCompressed := new(bytes.Buffer) 102 103 gw := gzip.NewWriter(dataCompressed) 104 defer gw.Close() 105 106 if _, err := io.Copy(gw, bytes.NewReader(data)); err != nil { 107 return 108 } 109 110 c.Set(key, dataCompressed.Bytes(), expiration) 111 } 112 113 // RefreshTTL can be used to update the TTL of an existing entry in the cache. 114 func (c *Redis) RefreshTTL(key string, expiration time.Duration) { 115 c.client.Expire(context.TODO(), key, expiration) 116 }