Files
apinto/resources/cache-local.go
黄孟柱 b7f71d87bb 优化
2022-10-24 18:44:17 +08:00

165 lines
3.3 KiB
Go

package resources
import (
"context"
"github.com/coocood/freecache"
"strconv"
"sync"
"time"
)
var (
localCache ICache = (*cacheLocal)(nil)
)
var (
once sync.Once
LocalCache func() ICache
)
func init() {
LocalCache = func() ICache {
once.Do(func() {
localCache = newCacher()
LocalCache = func() ICache {
return localCache
}
})
return localCache
}
}
type cacheLocal struct {
txLock sync.Mutex
keyLock sync.Mutex
keyLocks map[string]*sync.Mutex
client *freecache.Cache
}
type cacheLocalTX struct {
*cacheLocal
}
func (n *cacheLocalTX) Tx() TX {
return n
}
func (n *cacheLocal) Tx() TX {
n.txLock.Lock()
return &cacheLocalTX{cacheLocal: n}
}
func (n *cacheLocal) Exec(ctx context.Context) error {
n.txLock.Unlock()
return nil
}
func (n *cacheLocal) Close() error {
n.client.Clear()
return nil
}
func (n *cacheLocal) Set(ctx context.Context, key string, value []byte, expiration time.Duration) StatusResult {
err := n.client.Set([]byte(key), value, int(expiration.Seconds()))
return NewStatusResult(err)
}
func (n *cacheLocal) SetNX(ctx context.Context, key string, value []byte, expiration time.Duration) BoolResult {
old, err := n.client.GetOrSet([]byte(key), value, int(expiration.Seconds()))
if err != nil {
return NewBoolResult(false, err)
}
return NewBoolResult(old == nil, nil)
}
func (n *cacheLocal) DecrBy(ctx context.Context, key string, decrement int64, expiration time.Duration) IntResult {
return n.IncrBy(ctx, key, -decrement, expiration)
}
func (n *cacheLocal) IncrBy(ctx context.Context, key string, incr int64, expiration time.Duration) IntResult {
n.keyLock.Lock()
lock, has := n.keyLocks[key]
if !has {
lock = new(sync.Mutex)
n.keyLocks[key] = lock
lock.Lock()
}
n.keyLock.Unlock()
if has {
lock.Lock()
}
defer func() {
lock.Unlock()
if n.keyLock.TryLock() {
if lock.TryLock() {
delete(n.keyLocks, key)
lock.Unlock()
}
n.keyLock.Unlock()
}
}()
v, err := n.client.Get([]byte(key))
if err != nil || len(v) != 8 {
v = ToBytes(incr)
err := n.client.Set([]byte(key), v, int(expiration.Seconds()))
if err != nil {
return NewIntResult(0, err)
}
return NewIntResult(incr, nil)
}
value := ToInt(v) + incr
v = ToBytes(value)
err = n.client.Set([]byte(key), v, int(expiration.Seconds()))
if err != nil {
return NewIntResult(0, err)
}
return NewIntResult(value, nil)
}
func ToInt(b []byte) int64 {
v, err := strconv.ParseInt(string(b), 10, 64)
if err != nil {
return 0
}
return v
}
func ToBytes(v int64) []byte {
return []byte(strconv.FormatInt(v, 10))
}
func (n *cacheLocal) Get(ctx context.Context, key string) StringResult {
data, err := n.client.Get([]byte(key))
if err != nil {
return nil
}
return NewStringResultBytes(data, err)
}
func (n *cacheLocal) GetDel(ctx context.Context, key string) StringResult {
bytes, err := n.client.Get([]byte(key))
if err != nil {
return NewStringResult("", err)
}
n.client.Del([]byte(key))
return NewStringResultBytes(bytes, nil)
}
func (n *cacheLocal) Del(ctx context.Context, keys ...string) IntResult {
var count int64 = 0
for _, key := range keys {
if n.client.Del([]byte(key)) {
count++
}
}
return NewIntResult(count, nil)
}
func newCacher() *cacheLocal {
return &cacheLocal{client: freecache.NewCache(0), keyLocks: make(map[string]*sync.Mutex)}
}