|
@@ -1,16 +1,23 @@
|
|
package local
|
|
package local
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
+ "github.com/pkg/errors"
|
|
"strings"
|
|
"strings"
|
|
"sync"
|
|
"sync"
|
|
"time"
|
|
"time"
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+type valueItem struct {
|
|
|
|
+ value string
|
|
|
|
+ expireStartTime time.Time
|
|
|
|
+ expireSec int64
|
|
|
|
+}
|
|
|
|
+
|
|
type Cache struct {
|
|
type Cache struct {
|
|
namespace string
|
|
namespace string
|
|
syncMap *sync.Map
|
|
syncMap *sync.Map
|
|
expireRoutineDoneChannelsMutex *sync.Mutex
|
|
expireRoutineDoneChannelsMutex *sync.Mutex
|
|
- expireRoutineDoneChannels []chan any
|
|
|
|
|
|
+ expireRoutineDoneChannels map[string]chan any
|
|
}
|
|
}
|
|
|
|
|
|
func New(namespace string) *Cache {
|
|
func New(namespace string) *Cache {
|
|
@@ -18,7 +25,7 @@ func New(namespace string) *Cache {
|
|
namespace: namespace,
|
|
namespace: namespace,
|
|
syncMap: new(sync.Map),
|
|
syncMap: new(sync.Map),
|
|
expireRoutineDoneChannelsMutex: new(sync.Mutex),
|
|
expireRoutineDoneChannelsMutex: new(sync.Mutex),
|
|
- expireRoutineDoneChannels: make([]chan any, 0),
|
|
|
|
|
|
+ expireRoutineDoneChannels: make(map[string]chan any),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -43,43 +50,42 @@ func Destroy(cache *Cache) {
|
|
}
|
|
}
|
|
|
|
|
|
func (cache *Cache) Set(key string, value string, expireSec int64) error {
|
|
func (cache *Cache) Set(key string, value string, expireSec int64) error {
|
|
- cache.syncMap.Store(cache.key2CacheKey(key), value)
|
|
|
|
|
|
+ item := &valueItem{
|
|
|
|
+ value: value,
|
|
|
|
+ expireStartTime: time.Now(),
|
|
|
|
+ expireSec: expireSec,
|
|
|
|
+ }
|
|
|
|
|
|
- if expireSec != 0 {
|
|
|
|
|
|
+ cache.syncMap.Store(cache.key2CacheKey(key), item)
|
|
|
|
+
|
|
|
|
+ if item.expireSec != 0 {
|
|
doneChan := make(chan any)
|
|
doneChan := make(chan any)
|
|
|
|
|
|
cache.expireRoutineDoneChannelsMutex.Lock()
|
|
cache.expireRoutineDoneChannelsMutex.Lock()
|
|
|
|
|
|
if cache.expireRoutineDoneChannels != nil {
|
|
if cache.expireRoutineDoneChannels != nil {
|
|
- cache.expireRoutineDoneChannels = append(cache.expireRoutineDoneChannels, doneChan)
|
|
|
|
|
|
+ cache.expireRoutineDoneChannels[cache.key2CacheKey(key)] = doneChan
|
|
}
|
|
}
|
|
|
|
|
|
cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
|
|
|
go func() {
|
|
go func() {
|
|
- timer := time.NewTimer(time.Second * time.Duration(expireSec))
|
|
|
|
|
|
+ timer := time.NewTimer(time.Second * time.Duration(item.expireSec))
|
|
|
|
|
|
defer func() {
|
|
defer func() {
|
|
timer.Stop()
|
|
timer.Stop()
|
|
|
|
|
|
cache.expireRoutineDoneChannelsMutex.Lock()
|
|
cache.expireRoutineDoneChannelsMutex.Lock()
|
|
|
|
|
|
- if cache.expireRoutineDoneChannels != nil {
|
|
|
|
- findIndex := -1
|
|
|
|
-
|
|
|
|
- for i, savedDoneChan := range cache.expireRoutineDoneChannels {
|
|
|
|
- if savedDoneChan == doneChan {
|
|
|
|
- findIndex = i
|
|
|
|
- close(savedDoneChan)
|
|
|
|
- savedDoneChan = nil
|
|
|
|
- break
|
|
|
|
- }
|
|
|
|
|
|
+ if cache.expireRoutineDoneChannels != nil && len(cache.expireRoutineDoneChannels) > 0 {
|
|
|
|
+ savedDoneChan, ok := cache.expireRoutineDoneChannels[cache.key2CacheKey(key)]
|
|
|
|
+ if !ok {
|
|
|
|
+ return
|
|
}
|
|
}
|
|
|
|
|
|
- if findIndex != -1 {
|
|
|
|
- cache.expireRoutineDoneChannels =
|
|
|
|
- append(cache.expireRoutineDoneChannels[:findIndex], cache.expireRoutineDoneChannels[findIndex+1:]...)
|
|
|
|
- }
|
|
|
|
|
|
+ close(savedDoneChan)
|
|
|
|
+ savedDoneChan = nil
|
|
|
|
+ delete(cache.expireRoutineDoneChannels, cache.key2CacheKey(key))
|
|
}
|
|
}
|
|
|
|
|
|
cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
@@ -145,6 +151,65 @@ func (cache *Cache) Clear() error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func (cache *Cache) Expire(key string, expireSec int64) error {
|
|
|
|
+ value, loaded := cache.syncMap.Load(cache.key2CacheKey(key))
|
|
|
|
+ if !loaded {
|
|
|
|
+ return errors.New("对应的键不存在")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ oldItem := value.(*valueItem)
|
|
|
|
+
|
|
|
|
+ // 超时时间修改为0,停止协程
|
|
|
|
+ if expireSec == 0 {
|
|
|
|
+ cache.expireRoutineDoneChannelsMutex.Lock()
|
|
|
|
+
|
|
|
|
+ doneChan, ok := cache.expireRoutineDoneChannels[cache.key2CacheKey(key)]
|
|
|
|
+ if ok {
|
|
|
|
+ doneChan <- nil
|
|
|
|
+ close(doneChan)
|
|
|
|
+ doneChan = nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ delete(cache.expireRoutineDoneChannels, cache.key2CacheKey(key))
|
|
|
|
+
|
|
|
|
+ cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cache.syncMap.Store(cache.key2CacheKey(key), &valueItem{
|
|
|
|
+ value: oldItem.value,
|
|
|
|
+ expireStartTime: time.Now(),
|
|
|
|
+ expireSec: expireSec,
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (cache *Cache) ExpireTime(key string) (int64, error) {
|
|
|
|
+ value, loaded := cache.syncMap.Load(cache.key2CacheKey(key))
|
|
|
|
+ if !loaded {
|
|
|
|
+ return 0, nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ oldItem := value.(*valueItem)
|
|
|
|
+
|
|
|
|
+ return oldItem.expireSec, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (cache *Cache) TTL(key string) (int64, error) {
|
|
|
|
+ value, loaded := cache.syncMap.Load(cache.key2CacheKey(key))
|
|
|
|
+ if !loaded {
|
|
|
|
+ return 0, nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ oldItem := value.(*valueItem)
|
|
|
|
+ remainSec := int64(oldItem.expireStartTime.Add(time.Duration(oldItem.expireSec) * time.Second).Sub(time.Now()).Seconds())
|
|
|
|
+ if remainSec <= 0 {
|
|
|
|
+ return 0, nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return remainSec, nil
|
|
|
|
+}
|
|
|
|
+
|
|
func (cache *Cache) key2CacheKey(key string) string {
|
|
func (cache *Cache) key2CacheKey(key string) string {
|
|
return cache.namespace + "::" + key
|
|
return cache.namespace + "::" + key
|
|
}
|
|
}
|