1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
package cache
import (
"fmt"
"reflect"
"sync"
"time"
"github.com/pmylund/go-cache"
"github.com/revel/revel"
)
type InMemoryCache struct {
cache cache.Cache // Only expose the methods we want to make available
mu sync.RWMutex // For increment / decrement prevent reads and writes
}
func NewInMemoryCache(defaultExpiration time.Duration) InMemoryCache {
return InMemoryCache{cache: *cache.New(defaultExpiration, time.Minute), mu: sync.RWMutex{}}
}
func (c InMemoryCache) Get(key string, ptrValue interface{}) error {
c.mu.RLock()
defer c.mu.RUnlock()
value, found := c.cache.Get(key)
if !found {
return ErrCacheMiss
}
v := reflect.ValueOf(ptrValue)
if v.Type().Kind() == reflect.Ptr && v.Elem().CanSet() {
v.Elem().Set(reflect.ValueOf(value))
return nil
}
err := fmt.Errorf("revel/cache: attempt to get %s, but can not set value %v", key, v)
revel.ERROR.Println(err)
return err
}
func (c InMemoryCache) GetMulti(keys ...string) (Getter, error) {
return c, nil
}
func (c InMemoryCache) Set(key string, value interface{}, expires time.Duration) error {
c.mu.Lock()
defer c.mu.Unlock()
// NOTE: go-cache understands the values of DEFAULT and FOREVER
c.cache.Set(key, value, expires)
return nil
}
func (c InMemoryCache) Add(key string, value interface{}, expires time.Duration) error {
c.mu.Lock()
defer c.mu.Unlock()
err := c.cache.Add(key, value, expires)
if err != nil {
return ErrNotStored
}
return err
}
func (c InMemoryCache) Replace(key string, value interface{}, expires time.Duration) error {
c.mu.Lock()
defer c.mu.Unlock()
if err := c.cache.Replace(key, value, expires); err != nil {
return ErrNotStored
}
return nil
}
func (c InMemoryCache) Delete(key string) error {
c.mu.RLock()
defer c.mu.RUnlock()
if _, found := c.cache.Get(key); !found {
return ErrCacheMiss
}
c.cache.Delete(key)
return nil
}
func (c InMemoryCache) Increment(key string, n uint64) (newValue uint64, err error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, found := c.cache.Get(key); !found {
return 0, ErrCacheMiss
}
if err = c.cache.Increment(key, int64(n)); err != nil {
return
}
return c.convertTypeToUint64(key)
}
func (c InMemoryCache) Decrement(key string, n uint64) (newValue uint64, err error) {
c.mu.Lock()
defer c.mu.Unlock()
if nv, err := c.convertTypeToUint64(key); err != nil {
return 0, err
} else {
// Stop from going below zero
if n > nv {
n = nv
}
}
if err = c.cache.Decrement(key, int64(n)); err != nil {
return
}
return c.convertTypeToUint64(key)
}
func (c InMemoryCache) Flush() error {
c.mu.Lock()
defer c.mu.Unlock()
c.cache.Flush()
return nil
}
// Fetches and returns the converted type to a uint64
func (c InMemoryCache) convertTypeToUint64(key string) (newValue uint64, err error) {
v, found := c.cache.Get(key)
if !found {
return newValue, ErrCacheMiss
}
switch v.(type) {
case int:
newValue = uint64(v.(int))
case int8:
newValue = uint64(v.(int8))
case int16:
newValue = uint64(v.(int16))
case int32:
newValue = uint64(v.(int32))
case int64:
newValue = uint64(v.(int64))
case uint:
newValue = uint64(v.(uint))
case uintptr:
newValue = uint64(v.(uintptr))
case uint8:
newValue = uint64(v.(uint8))
case uint16:
newValue = uint64(v.(uint16))
case uint32:
newValue = uint64(v.(uint32))
case uint64:
newValue = uint64(v.(uint64))
case float32:
newValue = uint64(v.(float32))
case float64:
newValue = uint64(v.(float64))
default:
err = ErrInvalidValue
}
return
}
|