|
@@ -11,23 +11,20 @@ type valueItem struct {
|
|
|
value string
|
|
|
expireStartTime time.Time
|
|
|
expireSec int64
|
|
|
+ expireDoneChan chan any
|
|
|
}
|
|
|
|
|
|
type Cache struct {
|
|
|
*sync.RWMutex
|
|
|
- namespace string
|
|
|
- cacheMap map[string]*valueItem
|
|
|
- expireRoutineDoneChannelsMutex *sync.Mutex
|
|
|
- expireRoutineDoneChannels map[string]chan any
|
|
|
+ namespace string
|
|
|
+ cacheMap map[string]*valueItem
|
|
|
}
|
|
|
|
|
|
func New(namespace string) *Cache {
|
|
|
return &Cache{
|
|
|
- RWMutex: new(sync.RWMutex),
|
|
|
- namespace: namespace,
|
|
|
- cacheMap: nil,
|
|
|
- expireRoutineDoneChannelsMutex: new(sync.Mutex),
|
|
|
- expireRoutineDoneChannels: make(map[string]chan any),
|
|
|
+ RWMutex: new(sync.RWMutex),
|
|
|
+ namespace: namespace,
|
|
|
+ cacheMap: make(map[string]*valueItem),
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -39,37 +36,50 @@ func Destroy(cache *Cache) {
|
|
|
cache.Lock()
|
|
|
defer cache.Unlock()
|
|
|
|
|
|
- cache.expireRoutineDoneChannelsMutex.Lock()
|
|
|
-
|
|
|
- for _, doneChan := range cache.expireRoutineDoneChannels {
|
|
|
- doneChan <- nil
|
|
|
- close(doneChan)
|
|
|
- doneChan = nil
|
|
|
+ for _, item := range cache.cacheMap {
|
|
|
+ if item.expireDoneChan != nil {
|
|
|
+ item.expireDoneChan <- nil
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- cache.expireRoutineDoneChannels = nil
|
|
|
-
|
|
|
- cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
-
|
|
|
cache.cacheMap = nil
|
|
|
}
|
|
|
|
|
|
func (cache *Cache) Set(key string, value string, expireSec int64) error {
|
|
|
+ cache.Lock()
|
|
|
+ defer cache.Unlock()
|
|
|
+
|
|
|
+ oldItem, ok := cache.cacheMap[cache.key2CacheKey(key)]
|
|
|
+ if ok {
|
|
|
+ cache.stopExpireRoutine(oldItem)
|
|
|
+
|
|
|
+ newItem := &valueItem{
|
|
|
+ value: value,
|
|
|
+ expireStartTime: time.Now(),
|
|
|
+ expireSec: expireSec,
|
|
|
+ }
|
|
|
+
|
|
|
+ cache.cacheMap[cache.key2CacheKey(key)] = newItem
|
|
|
+
|
|
|
+ if expireSec == 0 {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ cache.startExpireRoutine(key, newItem)
|
|
|
+
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
item := &valueItem{
|
|
|
value: value,
|
|
|
expireStartTime: time.Now(),
|
|
|
expireSec: expireSec,
|
|
|
}
|
|
|
|
|
|
- cache.Lock()
|
|
|
- defer cache.Unlock()
|
|
|
-
|
|
|
cache.cacheMap[cache.key2CacheKey(key)] = item
|
|
|
|
|
|
if item.expireSec != 0 {
|
|
|
- cache.expireRoutineDoneChannelsMutex.Lock()
|
|
|
- cache.startExpireRoutineWithoutLock(key, item)
|
|
|
- cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
+ cache.startExpireRoutine(key, item)
|
|
|
}
|
|
|
|
|
|
return nil
|
|
@@ -138,16 +148,13 @@ func (cache *Cache) Expire(key string, expireSec int64) error {
|
|
|
cache.Lock()
|
|
|
defer cache.Unlock()
|
|
|
|
|
|
- cache.expireRoutineDoneChannelsMutex.Lock()
|
|
|
- defer cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
-
|
|
|
- cache.stopExpireRoutineWithoutLock(key)
|
|
|
-
|
|
|
oldItem, ok := cache.cacheMap[cache.key2CacheKey(key)]
|
|
|
if !ok {
|
|
|
- return errors.New("对应的键不存在")
|
|
|
+ return errors.New("对应的键不存在: " + key)
|
|
|
}
|
|
|
|
|
|
+ cache.stopExpireRoutine(oldItem)
|
|
|
+
|
|
|
newItem := &valueItem{
|
|
|
value: oldItem.value,
|
|
|
expireStartTime: time.Now(),
|
|
@@ -160,23 +167,11 @@ func (cache *Cache) Expire(key string, expireSec int64) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
- cache.startExpireRoutineWithoutLock(key, newItem)
|
|
|
+ cache.startExpireRoutine(key, newItem)
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (cache *Cache) ExpireTime(key string) (int64, error) {
|
|
|
- cache.RLock()
|
|
|
- defer cache.RUnlock()
|
|
|
-
|
|
|
- item, ok := cache.cacheMap[cache.key2CacheKey(key)]
|
|
|
- if !ok {
|
|
|
- return 0, nil
|
|
|
- }
|
|
|
-
|
|
|
- return item.expireSec, nil
|
|
|
-}
|
|
|
-
|
|
|
func (cache *Cache) TTL(key string) (int64, error) {
|
|
|
cache.RLock()
|
|
|
defer cache.RUnlock()
|
|
@@ -194,33 +189,17 @@ func (cache *Cache) TTL(key string) (int64, error) {
|
|
|
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
|
|
|
- }
|
|
|
+func (cache *Cache) startExpireRoutine(key string, item *valueItem) {
|
|
|
+ item.expireDoneChan = make(chan any)
|
|
|
|
|
|
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))
|
|
|
+ if item.expireDoneChan != nil {
|
|
|
+ close(item.expireDoneChan)
|
|
|
+ item.expireDoneChan = nil
|
|
|
}
|
|
|
-
|
|
|
- cache.expireRoutineDoneChannelsMutex.Unlock()
|
|
|
}()
|
|
|
|
|
|
for {
|
|
@@ -228,22 +207,18 @@ func (cache *Cache) startExpireRoutineWithoutLock(key string, item *valueItem) {
|
|
|
case <-timer.C:
|
|
|
delete(cache.cacheMap, cache.key2CacheKey(key))
|
|
|
return
|
|
|
- case <-doneChan:
|
|
|
+ case <-item.expireDoneChan:
|
|
|
+ timer.Stop()
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
}()
|
|
|
}
|
|
|
|
|
|
-func (cache *Cache) stopExpireRoutineWithoutLock(key string) {
|
|
|
- doneChan, ok := cache.expireRoutineDoneChannels[cache.key2CacheKey(key)]
|
|
|
- if ok {
|
|
|
- doneChan <- nil
|
|
|
- close(doneChan)
|
|
|
- doneChan = nil
|
|
|
+func (cache *Cache) stopExpireRoutine(item *valueItem) {
|
|
|
+ if item.expireDoneChan != nil {
|
|
|
+ item.expireDoneChan <- nil
|
|
|
}
|
|
|
-
|
|
|
- delete(cache.expireRoutineDoneChannels, cache.key2CacheKey(key))
|
|
|
}
|
|
|
|
|
|
func (cache *Cache) key2CacheKey(key string) string {
|