github.com/wormhole-foundation/wormhole-explorer/common@v0.0.0-20240604151348-09585b5b97c5/client/cache/cache.go (about)

     1  // Package cache implement a simple cache redis client.
     2  // It define a type [Cache] that represent the cache client and
     3  // It define the methods Get to get a valur from a cache key.
     4  package cache
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"time"
    11  
    12  	"github.com/go-redis/redis/v8"
    13  	"go.uber.org/zap"
    14  )
    15  
    16  var (
    17  	ErrCacheNotEnabled = errors.New("CACHE NOT ENABLED")
    18  	ErrNotFound        = errors.New("KEY NOT FOUND IN CACHE")
    19  	ErrInternal        = errors.New("INTERNAL CACHE ERROR")
    20  )
    21  
    22  // CacheClient redis cache client.
    23  type CacheClient struct {
    24  	Client  *redis.Client
    25  	Enabled bool
    26  	logger  *zap.Logger
    27  	Prefix  string
    28  }
    29  
    30  // Cache is the interface for cache client.
    31  type Cache interface {
    32  	CacheReadable
    33  	CacheWriteable
    34  }
    35  
    36  // CacheWriteable is the interface for write cache.
    37  type CacheWriteable interface {
    38  	Set(ctx context.Context, key string, value interface{}, expirations time.Duration) error
    39  }
    40  
    41  // CacheReadable is the interface for read cache.
    42  type CacheReadable interface {
    43  	Get(ctx context.Context, key string) (string, error)
    44  	Close() error
    45  }
    46  
    47  type CacheGetFunc func(ctx context.Context, key string) (string, error)
    48  
    49  // NewCacheClient init a new cache client.
    50  func NewCacheClient(redisClient *redis.Client, enabled bool, prefix string, log *zap.Logger) (*CacheClient, error) {
    51  	if redisClient == nil {
    52  		return nil, errors.New("redis client is nil")
    53  	}
    54  	return &CacheClient{Client: redisClient, Enabled: enabled, logger: log, Prefix: prefix}, nil
    55  }
    56  
    57  // Get get a cache value or error from a key.
    58  // If the cache is not enabled, the error value
    59  // If the cache not contain a value from a key, the error value errors.ErrNotFound is returned.
    60  // If exist some internal error in the cache, the error value errros.ErrInternalError is returned.
    61  func (c *CacheClient) Get(ctx context.Context, key string) (string, error) {
    62  	if !c.Enabled {
    63  		return "", ErrCacheNotEnabled
    64  	}
    65  	key = c.renderKey(key)
    66  	value, err := c.Client.Get(ctx, key).Result()
    67  	if err != nil {
    68  		requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
    69  		if errors.Is(err, redis.Nil) {
    70  			c.logger.Debug("key does not exist in cache",
    71  				zap.Error(err), zap.String("key", key), zap.String("requestID", requestID))
    72  			return "", ErrNotFound
    73  		}
    74  		c.logger.Error("error getting key from cache",
    75  			zap.Error(err), zap.String("key", key), zap.String("requestID", requestID))
    76  		return "", ErrInternal
    77  	}
    78  	return value, nil
    79  }
    80  
    81  // Close close the cache client.
    82  func (c *CacheClient) Close() error {
    83  	return c.Client.Close()
    84  }
    85  
    86  // Set set a value in cache.
    87  func (c *CacheClient) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
    88  	if !c.Enabled {
    89  		return ErrCacheNotEnabled
    90  	}
    91  	key = c.renderKey(key)
    92  	err := c.Client.Set(ctx, key, value, expiration).Err()
    93  	if err != nil {
    94  		requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
    95  		c.logger.Error("can not set key/value in cache",
    96  			zap.Error(err),
    97  			zap.String("key", key),
    98  			zap.Any("value", value),
    99  			zap.String("requestID", requestID))
   100  		return err
   101  	}
   102  	return nil
   103  }
   104  
   105  func (c *CacheClient) renderKey(key string) string {
   106  	if c.Prefix != "" {
   107  		return fmt.Sprintf("%s:%s", c.Prefix, key)
   108  	}
   109  	return key
   110  }