181 lines
3.9 KiB
Go
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
|
|
}
|