1// Copyright (c) The Thanos Authors. 2// Licensed under the Apache License 2.0. 3 4package cache 5 6import ( 7 "context" 8 "time" 9 10 "github.com/go-kit/kit/log" 11 "github.com/go-kit/kit/log/level" 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/prometheus/client_golang/prometheus/promauto" 14 15 "github.com/thanos-io/thanos/pkg/cacheutil" 16) 17 18// MemcachedCache is a memcached-based cache. 19type MemcachedCache struct { 20 logger log.Logger 21 memcached cacheutil.MemcachedClient 22 23 // Metrics. 24 requests prometheus.Counter 25 hits prometheus.Counter 26} 27 28// NewMemcachedCache makes a new MemcachedCache. 29func NewMemcachedCache(name string, logger log.Logger, memcached cacheutil.MemcachedClient, reg prometheus.Registerer) *MemcachedCache { 30 c := &MemcachedCache{ 31 logger: logger, 32 memcached: memcached, 33 } 34 35 c.requests = promauto.With(reg).NewCounter(prometheus.CounterOpts{ 36 Name: "thanos_cache_memcached_requests_total", 37 Help: "Total number of items requests to memcached.", 38 ConstLabels: prometheus.Labels{"name": name}, 39 }) 40 41 c.hits = promauto.With(reg).NewCounter(prometheus.CounterOpts{ 42 Name: "thanos_cache_memcached_hits_total", 43 Help: "Total number of items requests to the cache that were a hit.", 44 ConstLabels: prometheus.Labels{"name": name}, 45 }) 46 47 level.Info(logger).Log("msg", "created memcached cache") 48 49 return c 50} 51 52// Store data identified by keys. 53// The function enqueues the request and returns immediately: the entry will be 54// asynchronously stored in the cache. 55func (c *MemcachedCache) Store(ctx context.Context, data map[string][]byte, ttl time.Duration) { 56 var ( 57 firstErr error 58 failed int 59 ) 60 61 for key, val := range data { 62 if err := c.memcached.SetAsync(ctx, key, val, ttl); err != nil { 63 failed++ 64 if firstErr == nil { 65 firstErr = err 66 } 67 } 68 } 69 70 if firstErr != nil { 71 level.Warn(c.logger).Log("msg", "failed to store one or more items into memcached", "failed", failed, "firstErr", firstErr) 72 } 73} 74 75// Fetch fetches multiple keys and returns a map containing cache hits, along with a list of missing keys. 76// In case of error, it logs and return an empty cache hits map. 77func (c *MemcachedCache) Fetch(ctx context.Context, keys []string) map[string][]byte { 78 // Fetch the keys from memcached in a single request. 79 c.requests.Add(float64(len(keys))) 80 results := c.memcached.GetMulti(ctx, keys) 81 c.hits.Add(float64(len(results))) 82 return results 83} 84