go-common/app/interface/main/mcn/tool/cache/cache.go
2019-04-22 18:49:16 +08:00

181 lines
3.9 KiB
Go

package cache
import (
"context"
"fmt"
"go-common/library/cache"
"go-common/library/cache/memcache"
"go-common/library/log"
"go-common/library/net/metadata"
"go-common/library/stat/prom"
"time"
)
//DataLoader cache interface
type DataLoader interface {
Key() (key string)
Value() (value interface{})
// LoadValue return value need cache
// if err, nothing will cache
// if value == nil, and IsNullCached is true, empty will be cached
LoadValue(c context.Context) (value interface{}, err error)
Expire() time.Duration
Desc() string
}
// Get
// Delete
// Add
//MCWrapper wrapper for mc
type MCWrapper struct {
mc *memcache.Pool
cache *cache.Cache
// 是否缓存空值,防止缓存穿透
IsNullCached bool
}
// null definition
const (
IsNull = 1
NotNull = 0
)
type cacheValue struct {
Null int8 `json:"n"` // not 0 means null
Value interface{} `json:"v"`
}
//IsNull return true is it's null
func (s *cacheValue) IsNull() bool {
return s.Null != NotNull
}
//New new memcache wrapper
func New(mc *memcache.Pool) *MCWrapper {
return &MCWrapper{
mc: mc,
cache: cache.New(10, 1024),
}
}
func (m *MCWrapper) addRaw(c context.Context, data DataLoader, cacheV *cacheValue) (err error) {
if data == nil {
return
}
conn := m.mc.Get(c)
defer conn.Close()
key := data.Key()
item := &memcache.Item{Key: key, Object: cacheV, Expiration: int32(data.Expire() / time.Second), Flags: memcache.FlagJSON}
if err = conn.Set(item); err != nil {
actionDesc := "Add" + data.Desc()
prom.BusinessErrCount.Incr("mc:" + actionDesc)
log.Errorv(c, log.KV(actionDesc, fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
log.Info("Add key ok, key=%s, null=%d", key, cacheV.Null)
return
}
//Add add cache data
func (m *MCWrapper) Add(c context.Context, data DataLoader) (err error) {
var cacheV = &cacheValue{
Value: data.Value(),
}
return m.addRaw(c, data, cacheV)
}
//Delete delete cache data
func (m *MCWrapper) Delete(c context.Context, data DataLoader) (err error) {
conn := m.mc.Get(c)
defer conn.Close()
key := data.Key()
if err = conn.Delete(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
actionDesc := "Del" + data.Desc()
prom.BusinessErrCount.Incr("mc:" + actionDesc)
log.Errorv(c, log.KV(actionDesc, fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
//Get get data
func (m *MCWrapper) Get(c context.Context, data DataLoader) (err error) {
_, err = m.getRaw(c, data)
return
}
func (m *MCWrapper) getRaw(c context.Context, data DataLoader) (v *cacheValue, err error) {
conn := m.mc.Get(c)
defer conn.Close()
key := data.Key()
value, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
actionDesc := "Cache" + data.Desc()
prom.BusinessErrCount.Incr("mc:" + actionDesc)
log.Errorv(c, log.KV(actionDesc, fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
var cacheV = cacheValue{
Value: data.Value(),
}
err = conn.Scan(value, &cacheV)
if err != nil {
actionDesc := "Cache" + data.Desc()
prom.BusinessErrCount.Incr("mc:" + actionDesc)
log.Errorv(c, log.KV(actionDesc, fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
v = &cacheV
return
}
//GetOrLoad get from cache, if not found, then call data.LoadValue to load
func (m *MCWrapper) GetOrLoad(c context.Context, data DataLoader) (err error) {
var v *cacheValue
v, err = m.getRaw(c, data)
if err != nil {
return
}
if v != nil && !v.IsNull() {
prom.CacheHit.Incr(data.Desc())
return
}
// 没有找到对应的缓存,需求去拉取
prom.CacheMiss.Incr(data.Desc())
res, err := data.LoadValue(c)
if err != nil {
return
}
// 没有查到值,并且不缓存空值
if res == nil && !m.IsNullCached {
return
}
var cacheV = &cacheValue{
Value: res,
}
if res == nil {
cacheV.Null = IsNull
}
m.cache.Save(func() {
m.addRaw(metadata.WithContext(c), data, cacheV)
})
return
}