|
@@ -14,16 +14,18 @@ type valueItem struct {
|
|
}
|
|
}
|
|
|
|
|
|
type Cache struct {
|
|
type Cache struct {
|
|
|
|
+ *sync.RWMutex
|
|
namespace string
|
|
namespace string
|
|
- syncMap *sync.Map
|
|
|
|
|
|
+ cacheMap map[string]*valueItem
|
|
expireRoutineDoneChannelsMutex *sync.Mutex
|
|
expireRoutineDoneChannelsMutex *sync.Mutex
|
|
expireRoutineDoneChannels map[string]chan any
|
|
expireRoutineDoneChannels map[string]chan any
|
|
}
|
|
}
|
|
|
|
|
|
func New(namespace string) *Cache {
|
|
func New(namespace string) *Cache {
|
|
return &Cache{
|
|
return &Cache{
|
|
|
|
+ RWMutex: new(sync.RWMutex),
|
|
namespace: namespace,
|
|
namespace: namespace,
|
|
- syncMap: new(sync.Map),
|
|
|
|
|
|
+ cacheMap: nil,
|
|
expireRoutineDoneChannelsMutex: new(sync.Mutex),
|
|
expireRoutineDoneChannelsMutex: new(sync.Mutex),
|
|
expireRoutineDoneChannels: make(map[string]chan any),
|
|
expireRoutineDoneChannels: make(map[string]chan any),
|
|
}
|
|
}
|
|
@@ -34,6 +36,9 @@ func Destroy(cache *Cache) {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ cache.Lock()
|
|
|
|
+ defer cache.Unlock()
|
|
|
|
+
|
|
cache.expireRoutineDoneChannelsMutex.Lock()
|
|
cache.expireRoutineDoneChannelsMutex.Lock()
|
|
|
|
|
|
for _, doneChan := range cache.expireRoutineDoneChannels {
|
|
for _, doneChan := range cache.expireRoutineDoneChannels {
|
|
@@ -46,7 +51,7 @@ func Destroy(cache *Cache) {
|
|
|
|
|
|
cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
|
|
|
- cache.syncMap = nil
|
|
|
|
|
|
+ cache.cacheMap = nil
|
|
}
|
|
}
|
|
|
|
|
|
func (cache *Cache) Set(key string, value string, expireSec int64) error {
|
|
func (cache *Cache) Set(key string, value string, expireSec int64) error {
|
|
@@ -56,153 +61,132 @@ func (cache *Cache) Set(key string, value string, expireSec int64) error {
|
|
expireSec: expireSec,
|
|
expireSec: expireSec,
|
|
}
|
|
}
|
|
|
|
|
|
- cache.syncMap.Store(cache.key2CacheKey(key), item)
|
|
|
|
|
|
+ cache.Lock()
|
|
|
|
+ defer cache.Unlock()
|
|
|
|
|
|
- if item.expireSec != 0 {
|
|
|
|
- doneChan := make(chan any)
|
|
|
|
|
|
+ cache.cacheMap[cache.key2CacheKey(key)] = item
|
|
|
|
|
|
|
|
+ if item.expireSec != 0 {
|
|
cache.expireRoutineDoneChannelsMutex.Lock()
|
|
cache.expireRoutineDoneChannelsMutex.Lock()
|
|
-
|
|
|
|
- if cache.expireRoutineDoneChannels != nil {
|
|
|
|
- cache.expireRoutineDoneChannels[cache.key2CacheKey(key)] = doneChan
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ cache.startExpireRoutineWithoutLock(key, item)
|
|
cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
-
|
|
|
|
- go func() {
|
|
|
|
- timer := time.NewTimer(time.Second * time.Duration(item.expireSec))
|
|
|
|
-
|
|
|
|
- defer func() {
|
|
|
|
- timer.Stop()
|
|
|
|
-
|
|
|
|
- cache.expireRoutineDoneChannelsMutex.Lock()
|
|
|
|
-
|
|
|
|
- if cache.expireRoutineDoneChannels != nil && len(cache.expireRoutineDoneChannels) > 0 {
|
|
|
|
- savedDoneChan, ok := cache.expireRoutineDoneChannels[cache.key2CacheKey(key)]
|
|
|
|
- if !ok {
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- close(savedDoneChan)
|
|
|
|
- savedDoneChan = nil
|
|
|
|
- delete(cache.expireRoutineDoneChannels, cache.key2CacheKey(key))
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
|
- }()
|
|
|
|
-
|
|
|
|
- for {
|
|
|
|
- select {
|
|
|
|
- case <-timer.C:
|
|
|
|
- cache.syncMap.Delete(cache.key2CacheKey(key))
|
|
|
|
- return
|
|
|
|
- case <-doneChan:
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }()
|
|
|
|
}
|
|
}
|
|
|
|
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
func (cache *Cache) Get(key string) (string, error) {
|
|
func (cache *Cache) Get(key string) (string, error) {
|
|
- value, loaded := cache.syncMap.Load(cache.key2CacheKey(key))
|
|
|
|
- if !loaded {
|
|
|
|
|
|
+ cache.RLock()
|
|
|
|
+ defer cache.RUnlock()
|
|
|
|
+
|
|
|
|
+ item, ok := cache.cacheMap[cache.key2CacheKey(key)]
|
|
|
|
+ if !ok {
|
|
return "", nil
|
|
return "", nil
|
|
}
|
|
}
|
|
|
|
|
|
- return value.(string), nil
|
|
|
|
|
|
+ return item.value, nil
|
|
}
|
|
}
|
|
|
|
|
|
func (cache *Cache) GetMulti(keys []string) (map[string]string, error) {
|
|
func (cache *Cache) GetMulti(keys []string) (map[string]string, error) {
|
|
|
|
+ cache.RLock()
|
|
|
|
+ defer cache.RUnlock()
|
|
|
|
+
|
|
result := make(map[string]string)
|
|
result := make(map[string]string)
|
|
|
|
|
|
for _, key := range keys {
|
|
for _, key := range keys {
|
|
- value, loaded := cache.syncMap.Load(cache.key2CacheKey(key))
|
|
|
|
- if !loaded {
|
|
|
|
|
|
+ item, ok := cache.cacheMap[cache.key2CacheKey(key)]
|
|
|
|
+ if !ok {
|
|
result[key] = ""
|
|
result[key] = ""
|
|
}
|
|
}
|
|
|
|
|
|
- result[key] = value.(string)
|
|
|
|
|
|
+ result[key] = item.value
|
|
}
|
|
}
|
|
|
|
|
|
return result, nil
|
|
return result, nil
|
|
}
|
|
}
|
|
|
|
|
|
func (cache *Cache) GetAll() (map[string]string, error) {
|
|
func (cache *Cache) GetAll() (map[string]string, error) {
|
|
|
|
+ cache.RLock()
|
|
|
|
+ defer cache.RUnlock()
|
|
|
|
+
|
|
result := make(map[string]string)
|
|
result := make(map[string]string)
|
|
|
|
|
|
- cache.syncMap.Range(func(key any, value any) bool {
|
|
|
|
- result[cache.cacheKey2Key(key.(string))] = value.(string)
|
|
|
|
- return true
|
|
|
|
- })
|
|
|
|
|
|
+ for key, item := range cache.cacheMap {
|
|
|
|
+ result[cache.cacheKey2Key(key)] = item.value
|
|
|
|
+ }
|
|
|
|
|
|
return result, nil
|
|
return result, nil
|
|
}
|
|
}
|
|
|
|
|
|
func (cache *Cache) Delete(key string) error {
|
|
func (cache *Cache) Delete(key string) error {
|
|
- cache.syncMap.Delete(cache.key2CacheKey(key))
|
|
|
|
|
|
+ cache.Lock()
|
|
|
|
+ defer cache.Unlock()
|
|
|
|
+
|
|
|
|
+ delete(cache.cacheMap, cache.key2CacheKey(key))
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
func (cache *Cache) Clear() error {
|
|
func (cache *Cache) Clear() error {
|
|
- cache.syncMap = new(sync.Map)
|
|
|
|
|
|
+ cache.Lock()
|
|
|
|
+ defer cache.Unlock()
|
|
|
|
+
|
|
|
|
+ cache.cacheMap = make(map[string]*valueItem)
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
func (cache *Cache) Expire(key string, expireSec int64) error {
|
|
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()
|
|
|
|
|
|
+ cache.Lock()
|
|
|
|
+ defer cache.Unlock()
|
|
|
|
|
|
- doneChan, ok := cache.expireRoutineDoneChannels[cache.key2CacheKey(key)]
|
|
|
|
- if ok {
|
|
|
|
- doneChan <- nil
|
|
|
|
- close(doneChan)
|
|
|
|
- doneChan = nil
|
|
|
|
- }
|
|
|
|
|
|
+ cache.expireRoutineDoneChannelsMutex.Lock()
|
|
|
|
+ defer cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
|
|
|
- delete(cache.expireRoutineDoneChannels, cache.key2CacheKey(key))
|
|
|
|
|
|
+ cache.stopExpireRoutineWithoutLock(key)
|
|
|
|
|
|
- cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
|
|
|
+ oldItem, ok := cache.cacheMap[cache.key2CacheKey(key)]
|
|
|
|
+ if !ok {
|
|
|
|
+ return errors.New("对应的键不存在")
|
|
}
|
|
}
|
|
|
|
|
|
- cache.syncMap.Store(cache.key2CacheKey(key), &valueItem{
|
|
|
|
|
|
+ newItem := &valueItem{
|
|
value: oldItem.value,
|
|
value: oldItem.value,
|
|
expireStartTime: time.Now(),
|
|
expireStartTime: time.Now(),
|
|
expireSec: expireSec,
|
|
expireSec: expireSec,
|
|
- })
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cache.cacheMap[cache.key2CacheKey(key)] = newItem
|
|
|
|
+
|
|
|
|
+ if expireSec == 0 {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cache.startExpireRoutineWithoutLock(key, newItem)
|
|
|
|
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
func (cache *Cache) ExpireTime(key string) (int64, error) {
|
|
func (cache *Cache) ExpireTime(key string) (int64, error) {
|
|
- value, loaded := cache.syncMap.Load(cache.key2CacheKey(key))
|
|
|
|
- if !loaded {
|
|
|
|
|
|
+ cache.RLock()
|
|
|
|
+ defer cache.RUnlock()
|
|
|
|
+
|
|
|
|
+ item, ok := cache.cacheMap[cache.key2CacheKey(key)]
|
|
|
|
+ if !ok {
|
|
return 0, nil
|
|
return 0, nil
|
|
}
|
|
}
|
|
|
|
|
|
- oldItem := value.(*valueItem)
|
|
|
|
-
|
|
|
|
- return oldItem.expireSec, nil
|
|
|
|
|
|
+ return item.expireSec, nil
|
|
}
|
|
}
|
|
|
|
|
|
func (cache *Cache) TTL(key string) (int64, error) {
|
|
func (cache *Cache) TTL(key string) (int64, error) {
|
|
- value, loaded := cache.syncMap.Load(cache.key2CacheKey(key))
|
|
|
|
- if !loaded {
|
|
|
|
|
|
+ cache.RLock()
|
|
|
|
+ defer cache.RUnlock()
|
|
|
|
+
|
|
|
|
+ item, ok := cache.cacheMap[cache.key2CacheKey(key)]
|
|
|
|
+ if !ok {
|
|
return 0, nil
|
|
return 0, nil
|
|
}
|
|
}
|
|
|
|
|
|
- oldItem := value.(*valueItem)
|
|
|
|
- remainSec := int64(oldItem.expireStartTime.Add(time.Duration(oldItem.expireSec) * time.Second).Sub(time.Now()).Seconds())
|
|
|
|
|
|
+ remainSec := int64(item.expireStartTime.Add(time.Duration(item.expireSec)*time.Second).Sub(time.Now()).Seconds() + 0.5)
|
|
if remainSec <= 0 {
|
|
if remainSec <= 0 {
|
|
return 0, nil
|
|
return 0, nil
|
|
}
|
|
}
|
|
@@ -210,6 +194,58 @@ func (cache *Cache) TTL(key string) (int64, error) {
|
|
return remainSec, nil
|
|
return remainSec, nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func (cache *Cache) startExpireRoutineWithoutLock(key string, item *valueItem) {
|
|
|
|
+ doneChan := make(chan any)
|
|
|
|
+
|
|
|
|
+ if cache.expireRoutineDoneChannels != nil {
|
|
|
|
+ cache.expireRoutineDoneChannels[cache.key2CacheKey(key)] = doneChan
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ go func() {
|
|
|
|
+ timer := time.NewTimer(time.Second * time.Duration(item.expireSec))
|
|
|
|
+
|
|
|
|
+ defer func() {
|
|
|
|
+ timer.Stop()
|
|
|
|
+
|
|
|
|
+ cache.expireRoutineDoneChannelsMutex.Lock()
|
|
|
|
+
|
|
|
|
+ if cache.expireRoutineDoneChannels != nil && len(cache.expireRoutineDoneChannels) > 0 {
|
|
|
|
+ savedDoneChan, ok := cache.expireRoutineDoneChannels[cache.key2CacheKey(key)]
|
|
|
|
+ if !ok {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ close(savedDoneChan)
|
|
|
|
+ savedDoneChan = nil
|
|
|
|
+ delete(cache.expireRoutineDoneChannels, cache.key2CacheKey(key))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ for {
|
|
|
|
+ select {
|
|
|
|
+ case <-timer.C:
|
|
|
|
+ delete(cache.cacheMap, cache.key2CacheKey(key))
|
|
|
|
+ return
|
|
|
|
+ case <-doneChan:
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (cache *Cache) stopExpireRoutineWithoutLock(key string) {
|
|
|
|
+ doneChan, ok := cache.expireRoutineDoneChannels[cache.key2CacheKey(key)]
|
|
|
|
+ if ok {
|
|
|
|
+ doneChan <- nil
|
|
|
|
+ close(doneChan)
|
|
|
|
+ doneChan = nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ delete(cache.expireRoutineDoneChannels, cache.key2CacheKey(key))
|
|
|
|
+}
|
|
|
|
+
|
|
func (cache *Cache) key2CacheKey(key string) string {
|
|
func (cache *Cache) key2CacheKey(key string) string {
|
|
return cache.namespace + "::" + key
|
|
return cache.namespace + "::" + key
|
|
}
|
|
}
|