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 }