package local import ( "strings" "sync" "time" ) type Cache struct { namespace string syncMap *sync.Map expireRoutineDoneChannelsMutex *sync.Mutex expireRoutineDoneChannels []chan any } func New(namespace string) *Cache { return &Cache{ namespace: namespace, syncMap: new(sync.Map), expireRoutineDoneChannelsMutex: new(sync.Mutex), expireRoutineDoneChannels: make([]chan any, 0), } } func Destroy(cache *Cache) { if cache == nil { return } cache.expireRoutineDoneChannelsMutex.Lock() for _, doneChan := range cache.expireRoutineDoneChannels { doneChan <- nil close(doneChan) doneChan = nil } cache.expireRoutineDoneChannels = nil cache.expireRoutineDoneChannelsMutex.Unlock() cache.syncMap = nil } func (cache *Cache) Set(key string, value string, expireSec int64) error { cache.syncMap.Store(cache.key2CacheKey(key), value) if expireSec != 0 { doneChan := make(chan any) cache.expireRoutineDoneChannelsMutex.Lock() if cache.expireRoutineDoneChannels != nil { cache.expireRoutineDoneChannels = append(cache.expireRoutineDoneChannels, doneChan) } cache.expireRoutineDoneChannelsMutex.Unlock() go func() { timer := time.NewTimer(time.Second * time.Duration(expireSec)) defer func() { timer.Stop() 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 findIndex != -1 { cache.expireRoutineDoneChannels = append(cache.expireRoutineDoneChannels[:findIndex], cache.expireRoutineDoneChannels[findIndex+1:]...) } } cache.expireRoutineDoneChannelsMutex.Unlock() }() for { select { case <-timer.C: cache.syncMap.Delete(cache.key2CacheKey(key)) return case <-doneChan: return } } }() } return nil } func (cache *Cache) Get(key string) (string, error) { value, loaded := cache.syncMap.Load(cache.key2CacheKey(key)) if !loaded { return "", nil } return value.(string), nil } func (cache *Cache) GetMulti(keys []string) (map[string]string, error) { result := make(map[string]string) for _, key := range keys { value, loaded := cache.syncMap.Load(cache.key2CacheKey(key)) if !loaded { result[key] = "" } result[key] = value.(string) } return result, nil } func (cache *Cache) GetAll() (map[string]string, error) { result := make(map[string]string) cache.syncMap.Range(func(key any, value any) bool { result[cache.cacheKey2Key(key.(string))] = value.(string) return true }) return result, nil } func (cache *Cache) Delete(key string) error { cache.syncMap.Delete(cache.key2CacheKey(key)) return nil } func (cache *Cache) Clear() error { cache.syncMap = new(sync.Map) return nil } func (cache *Cache) key2CacheKey(key string) string { return cache.namespace + "::" + key } func (cache *Cache) cacheKey2Key(cacheKey string) string { return strings.TrimPrefix(cacheKey, cache.namespace+"::") }