github.com/go-graphite/carbonapi@v0.17.0/cache/cache.go (about) 1 package cache 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "errors" 7 "sync/atomic" 8 "time" 9 10 "github.com/ansel1/merry" 11 12 "github.com/bradfitz/gomemcache/memcache" 13 14 "github.com/dgryski/go-expirecache" 15 ) 16 17 var ( 18 ErrTimeout = errors.New("cache: timeout") 19 ErrNotFound = errors.New("cache: not found") 20 ) 21 22 type BytesCache interface { 23 Get(k string) ([]byte, error) 24 Set(k string, v []byte, expire int32) 25 } 26 27 type NullCache struct{} 28 29 func (NullCache) Get(string) ([]byte, error) { return nil, ErrNotFound } 30 func (NullCache) Set(string, []byte, int32) {} 31 32 func NewExpireCache(maxsize uint64) BytesCache { 33 ec := expirecache.New(maxsize) 34 go ec.ApproximateCleaner(10 * time.Second) 35 return &ExpireCache{ec: ec} 36 } 37 38 type ExpireCache struct { 39 ec *expirecache.Cache 40 } 41 42 func (ec ExpireCache) Get(k string) ([]byte, error) { 43 v, ok := ec.ec.Get(k) 44 45 if !ok { 46 return nil, ErrNotFound 47 } 48 49 return v.([]byte), nil 50 } 51 52 func (ec ExpireCache) Set(k string, v []byte, expire int32) { 53 ec.ec.Set(k, v, uint64(len(v)), expire) 54 } 55 56 func (ec ExpireCache) Items() int { return ec.ec.Items() } 57 58 func (ec ExpireCache) Size() uint64 { return ec.ec.Size() } 59 60 func NewMemcached(prefix string, servers ...string) BytesCache { 61 return &MemcachedCache{prefix: prefix, client: memcache.New(servers...)} 62 } 63 64 type MemcachedCache struct { 65 prefix string 66 client *memcache.Client 67 timeouts uint64 68 } 69 70 func (m *MemcachedCache) Get(k string) ([]byte, error) { 71 key := sha256.Sum256([]byte(k)) 72 hk := hex.EncodeToString(key[:]) 73 done := make(chan bool, 1) 74 75 var err error 76 var item *memcache.Item 77 78 go func() { 79 item, err = m.client.Get(m.prefix + hk) 80 done <- true 81 }() 82 83 timeout := time.After(50 * time.Millisecond) 84 85 select { 86 case <-timeout: 87 atomic.AddUint64(&m.timeouts, 1) 88 return nil, ErrTimeout 89 case <-done: 90 } 91 92 if err != nil { 93 // translate to internal cache miss error 94 if merry.Is(err, memcache.ErrCacheMiss) { 95 err = ErrNotFound 96 } 97 return nil, err 98 } 99 100 return item.Value, nil 101 } 102 103 func (m *MemcachedCache) Set(k string, v []byte, expire int32) { 104 key := sha256.Sum256([]byte(k)) 105 hk := hex.EncodeToString(key[:]) 106 go func() { 107 _ = m.client.Set(&memcache.Item{Key: m.prefix + hk, Value: v, Expiration: expire}) 108 }() 109 } 110 111 func (m *MemcachedCache) Timeouts() uint64 { 112 return atomic.LoadUint64(&m.timeouts) 113 }