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  }