github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/framework/cache/memory.go (about) 1 // The package is migrated from beego, you can get from following link: 2 // import( 3 // "github.com/beego/beego/v2/client/cache" 4 // ) 5 // Copyright 2023. All Rights Reserved. 6 // 7 // Licensed under the Apache License, Version 2.0 (the "License"); 8 // you may not use this file except in compliance with the License. 9 // You may obtain a copy of the License at 10 // 11 // http://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an "AS IS" BASIS, 15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 // See the License for the specific language governing permissions and 17 // limitations under the License. 18 19 package cache 20 21 import ( 22 "context" 23 "encoding/json" 24 "fmt" 25 "strings" 26 "sync" 27 "time" 28 29 "github.com/mdaxf/iac/framework/berror" 30 ) 31 32 // DefaultEvery sets a timer for how often to recycle the expired cache items in memory (in seconds) 33 var DefaultEvery = 60 // 1 minute 34 35 // MemoryItem stores memory cache item. 36 type MemoryItem struct { 37 val interface{} 38 createdTime time.Time 39 lifespan time.Duration 40 } 41 42 func (mi *MemoryItem) isExpire() bool { 43 // 0 means forever 44 if mi.lifespan == 0 { 45 return false 46 } 47 return time.Since(mi.createdTime) > mi.lifespan 48 } 49 50 // MemoryCache is a memory cache adapter. 51 // Contains a RW locker for safe map storage. 52 type MemoryCache struct { 53 sync.RWMutex 54 dur time.Duration 55 items map[string]*MemoryItem 56 Every int // run an expiration check Every clock time 57 } 58 59 // NewMemoryCache returns a new MemoryCache. 60 func NewMemoryCache() Cache { 61 cache := MemoryCache{items: make(map[string]*MemoryItem)} 62 return &cache 63 } 64 65 // Get returns cache from memory. 66 // If non-existent or expired, return nil. 67 func (bc *MemoryCache) Get(ctx context.Context, key string) (interface{}, error) { 68 bc.RLock() 69 defer bc.RUnlock() 70 if itm, ok := bc.items[key]; ok { 71 if itm.isExpire() { 72 return nil, ErrKeyExpired 73 } 74 return itm.val, nil 75 } 76 return nil, ErrKeyNotExist 77 } 78 79 // GetMulti gets caches from memory. 80 // If non-existent or expired, return nil. 81 func (bc *MemoryCache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) { 82 rc := make([]interface{}, len(keys)) 83 keysErr := make([]string, 0) 84 85 for i, ki := range keys { 86 val, err := bc.Get(context.Background(), ki) 87 if err != nil { 88 keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, err.Error())) 89 continue 90 } 91 rc[i] = val 92 } 93 94 if len(keysErr) == 0 { 95 return rc, nil 96 } 97 return rc, berror.Error(MultiGetFailed, strings.Join(keysErr, "; ")) 98 } 99 100 // Put puts cache into memory. 101 // If lifespan is 0, it will never overwrite this value unless restarted 102 func (bc *MemoryCache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error { 103 bc.Lock() 104 defer bc.Unlock() 105 bc.items[key] = &MemoryItem{ 106 val: val, 107 createdTime: time.Now(), 108 lifespan: timeout, 109 } 110 return nil 111 } 112 113 // Delete cache in memory. 114 // If the key is not found, it will not return error 115 func (bc *MemoryCache) Delete(ctx context.Context, key string) error { 116 bc.Lock() 117 defer bc.Unlock() 118 delete(bc.items, key) 119 return nil 120 } 121 122 // Incr increases cache counter in memory. 123 // Supports int,int32,int64,uint,uint32,uint64. 124 func (bc *MemoryCache) Incr(ctx context.Context, key string) error { 125 bc.Lock() 126 defer bc.Unlock() 127 itm, ok := bc.items[key] 128 if !ok { 129 return ErrKeyNotExist 130 } 131 132 val, err := incr(itm.val) 133 if err != nil { 134 return err 135 } 136 itm.val = val 137 return nil 138 } 139 140 // Decr decreases counter in memory. 141 func (bc *MemoryCache) Decr(ctx context.Context, key string) error { 142 bc.Lock() 143 defer bc.Unlock() 144 itm, ok := bc.items[key] 145 if !ok { 146 return ErrKeyNotExist 147 } 148 149 val, err := decr(itm.val) 150 if err != nil { 151 return err 152 } 153 itm.val = val 154 return nil 155 } 156 157 // IsExist checks if cache exists in memory. 158 func (bc *MemoryCache) IsExist(ctx context.Context, key string) (bool, error) { 159 bc.RLock() 160 defer bc.RUnlock() 161 if v, ok := bc.items[key]; ok { 162 return !v.isExpire(), nil 163 } 164 return false, nil 165 } 166 167 // ClearAll deletes all cache in memory. 168 func (bc *MemoryCache) ClearAll(context.Context) error { 169 bc.Lock() 170 defer bc.Unlock() 171 bc.items = make(map[string]*MemoryItem) 172 return nil 173 } 174 175 // StartAndGC starts memory cache. Checks expiration in every clock time. 176 func (bc *MemoryCache) StartAndGC(config string) error { 177 var cf map[string]int 178 if err := json.Unmarshal([]byte(config), &cf); err != nil { 179 return berror.Wrapf(err, InvalidMemoryCacheCfg, "invalid config, please check your input: %s", config) 180 } 181 if _, ok := cf["interval"]; !ok { 182 cf = make(map[string]int) 183 cf["interval"] = DefaultEvery 184 } 185 dur := time.Duration(cf["interval"]) * time.Second 186 bc.Every = cf["interval"] 187 bc.dur = dur 188 go bc.vacuum() 189 return nil 190 } 191 192 // check expiration. 193 func (bc *MemoryCache) vacuum() { 194 bc.RLock() 195 every := bc.Every 196 bc.RUnlock() 197 198 if every < 1 { 199 return 200 } 201 for { 202 <-time.After(bc.dur) 203 bc.RLock() 204 if bc.items == nil { 205 bc.RUnlock() 206 return 207 } 208 bc.RUnlock() 209 if keys := bc.expiredKeys(); len(keys) != 0 { 210 bc.clearItems(keys) 211 } 212 } 213 } 214 215 // expiredKeys returns keys list which are expired. 216 func (bc *MemoryCache) expiredKeys() (keys []string) { 217 bc.RLock() 218 defer bc.RUnlock() 219 for key, itm := range bc.items { 220 if itm.isExpire() { 221 keys = append(keys, key) 222 } 223 } 224 return 225 } 226 227 // ClearItems removes all items who's key is in keys 228 func (bc *MemoryCache) clearItems(keys []string) { 229 bc.Lock() 230 defer bc.Unlock() 231 for _, key := range keys { 232 delete(bc.items, key) 233 } 234 } 235 236 func init() { 237 Register("memory", NewMemoryCache) 238 }