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  }