github.com/thanos-io/thanos@v0.32.5/internal/cortex/chunk/cache/redis_cache.go (about) 1 // Copyright (c) The Cortex Authors. 2 // Licensed under the Apache License 2.0. 3 4 package cache 5 6 import ( 7 "context" 8 9 "github.com/go-kit/log" 10 "github.com/go-kit/log/level" 11 otlog "github.com/opentracing/opentracing-go/log" 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/prometheus/client_golang/prometheus/promauto" 14 instr "github.com/weaveworks/common/instrument" 15 16 "github.com/thanos-io/thanos/internal/cortex/util/spanlogger" 17 ) 18 19 // RedisCache type caches chunks in redis 20 type RedisCache struct { 21 name string 22 redis *RedisClient 23 logger log.Logger 24 requestDuration *instr.HistogramCollector 25 } 26 27 // NewRedisCache creates a new RedisCache 28 func NewRedisCache(name string, redisClient *RedisClient, reg prometheus.Registerer, logger log.Logger) *RedisCache { 29 cache := &RedisCache{ 30 name: name, 31 redis: redisClient, 32 logger: logger, 33 requestDuration: instr.NewHistogramCollector( 34 promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{ 35 Namespace: "cortex", 36 Name: "rediscache_request_duration_seconds", 37 Help: "Total time spent in seconds doing Redis requests.", 38 Buckets: prometheus.ExponentialBuckets(0.000016, 4, 8), 39 ConstLabels: prometheus.Labels{"name": name}, 40 }, []string{"method", "status_code"}), 41 ), 42 } 43 if err := cache.redis.Ping(context.Background()); err != nil { 44 level.Error(logger).Log("msg", "error connecting to redis", "name", name, "err", err) 45 } 46 return cache 47 } 48 49 func redisStatusCode(err error) string { 50 // TODO: Figure out if there are more error types returned by Redis 51 switch err { 52 case nil: 53 return "200" 54 default: 55 return "500" 56 } 57 } 58 59 // Fetch gets keys from the cache. The keys that are found must be in the order of the keys requested. 60 func (c *RedisCache) Fetch(ctx context.Context, keys []string) (found []string, bufs [][]byte, missed []string) { 61 const method = "RedisCache.MGet" 62 var items [][]byte 63 // Run a tracked request, using c.requestDuration to monitor requests. 64 err := instr.CollectedRequest(ctx, method, c.requestDuration, redisStatusCode, func(ctx context.Context) error { 65 log, _ := spanlogger.New(ctx, method) 66 defer log.Finish() 67 log.LogFields(otlog.Int("keys requested", len(keys))) 68 69 var err error 70 items, err = c.redis.MGet(ctx, keys) 71 if err != nil { 72 log.Error(err) 73 level.Error(c.logger).Log("msg", "failed to get from redis", "name", c.name, "err", err) 74 return err 75 } 76 77 log.LogFields(otlog.Int("keys found", len(items))) 78 79 return nil 80 }) 81 if err != nil { 82 return found, bufs, keys 83 } 84 85 for i, key := range keys { 86 if items[i] != nil { 87 found = append(found, key) 88 bufs = append(bufs, items[i]) 89 } else { 90 missed = append(missed, key) 91 } 92 } 93 94 return 95 } 96 97 // Store stores the key in the cache. 98 func (c *RedisCache) Store(ctx context.Context, keys []string, bufs [][]byte) { 99 err := c.redis.MSet(ctx, keys, bufs) 100 if err != nil { 101 level.Error(c.logger).Log("msg", "failed to put to redis", "name", c.name, "err", err) 102 } 103 } 104 105 // Stop stops the redis client. 106 func (c *RedisCache) Stop() { 107 _ = c.redis.Close() 108 }