github.com/astaxie/beego@v1.12.3/cache/memory.go (about) 1 // Copyright 2014 beego Author. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cache 16 17 import ( 18 "encoding/json" 19 "errors" 20 "sync" 21 "time" 22 ) 23 24 var ( 25 // DefaultEvery means the clock time of recycling the expired cache items in memory. 26 DefaultEvery = 60 // 1 minute 27 ) 28 29 // MemoryItem store memory cache item. 30 type MemoryItem struct { 31 val interface{} 32 createdTime time.Time 33 lifespan time.Duration 34 } 35 36 func (mi *MemoryItem) isExpire() bool { 37 // 0 means forever 38 if mi.lifespan == 0 { 39 return false 40 } 41 return time.Now().Sub(mi.createdTime) > mi.lifespan 42 } 43 44 // MemoryCache is Memory cache adapter. 45 // it contains a RW locker for safe map storage. 46 type MemoryCache struct { 47 sync.RWMutex 48 dur time.Duration 49 items map[string]*MemoryItem 50 Every int // run an expiration check Every clock time 51 } 52 53 // NewMemoryCache returns a new MemoryCache. 54 func NewMemoryCache() Cache { 55 cache := MemoryCache{items: make(map[string]*MemoryItem)} 56 return &cache 57 } 58 59 // Get cache from memory. 60 // if non-existed or expired, return nil. 61 func (bc *MemoryCache) Get(name string) interface{} { 62 bc.RLock() 63 defer bc.RUnlock() 64 if itm, ok := bc.items[name]; ok { 65 if itm.isExpire() { 66 return nil 67 } 68 return itm.val 69 } 70 return nil 71 } 72 73 // GetMulti gets caches from memory. 74 // if non-existed or expired, return nil. 75 func (bc *MemoryCache) GetMulti(names []string) []interface{} { 76 var rc []interface{} 77 for _, name := range names { 78 rc = append(rc, bc.Get(name)) 79 } 80 return rc 81 } 82 83 // Put cache to memory. 84 // if lifespan is 0, it will be forever till restart. 85 func (bc *MemoryCache) Put(name string, value interface{}, lifespan time.Duration) error { 86 bc.Lock() 87 defer bc.Unlock() 88 bc.items[name] = &MemoryItem{ 89 val: value, 90 createdTime: time.Now(), 91 lifespan: lifespan, 92 } 93 return nil 94 } 95 96 // Delete cache in memory. 97 func (bc *MemoryCache) Delete(name string) error { 98 bc.Lock() 99 defer bc.Unlock() 100 if _, ok := bc.items[name]; !ok { 101 return errors.New("key not exist") 102 } 103 delete(bc.items, name) 104 if _, ok := bc.items[name]; ok { 105 return errors.New("delete key error") 106 } 107 return nil 108 } 109 110 // Incr increase cache counter in memory. 111 // it supports int,int32,int64,uint,uint32,uint64. 112 func (bc *MemoryCache) Incr(key string) error { 113 bc.Lock() 114 defer bc.Unlock() 115 itm, ok := bc.items[key] 116 if !ok { 117 return errors.New("key not exist") 118 } 119 switch val := itm.val.(type) { 120 case int: 121 itm.val = val + 1 122 case int32: 123 itm.val = val + 1 124 case int64: 125 itm.val = val + 1 126 case uint: 127 itm.val = val + 1 128 case uint32: 129 itm.val = val + 1 130 case uint64: 131 itm.val = val + 1 132 default: 133 return errors.New("item val is not (u)int (u)int32 (u)int64") 134 } 135 return nil 136 } 137 138 // Decr decrease counter in memory. 139 func (bc *MemoryCache) Decr(key string) error { 140 bc.Lock() 141 defer bc.Unlock() 142 itm, ok := bc.items[key] 143 if !ok { 144 return errors.New("key not exist") 145 } 146 switch val := itm.val.(type) { 147 case int: 148 itm.val = val - 1 149 case int64: 150 itm.val = val - 1 151 case int32: 152 itm.val = val - 1 153 case uint: 154 if val > 0 { 155 itm.val = val - 1 156 } else { 157 return errors.New("item val is less than 0") 158 } 159 case uint32: 160 if val > 0 { 161 itm.val = val - 1 162 } else { 163 return errors.New("item val is less than 0") 164 } 165 case uint64: 166 if val > 0 { 167 itm.val = val - 1 168 } else { 169 return errors.New("item val is less than 0") 170 } 171 default: 172 return errors.New("item val is not int int64 int32") 173 } 174 return nil 175 } 176 177 // IsExist check cache exist in memory. 178 func (bc *MemoryCache) IsExist(name string) bool { 179 bc.RLock() 180 defer bc.RUnlock() 181 if v, ok := bc.items[name]; ok { 182 return !v.isExpire() 183 } 184 return false 185 } 186 187 // ClearAll will delete all cache in memory. 188 func (bc *MemoryCache) ClearAll() error { 189 bc.Lock() 190 defer bc.Unlock() 191 bc.items = make(map[string]*MemoryItem) 192 return nil 193 } 194 195 // StartAndGC start memory cache. it will check expiration in every clock time. 196 func (bc *MemoryCache) StartAndGC(config string) error { 197 var cf map[string]int 198 json.Unmarshal([]byte(config), &cf) 199 if _, ok := cf["interval"]; !ok { 200 cf = make(map[string]int) 201 cf["interval"] = DefaultEvery 202 } 203 dur := time.Duration(cf["interval"]) * time.Second 204 bc.Every = cf["interval"] 205 bc.dur = dur 206 go bc.vacuum() 207 return nil 208 } 209 210 // check expiration. 211 func (bc *MemoryCache) vacuum() { 212 bc.RLock() 213 every := bc.Every 214 bc.RUnlock() 215 216 if every < 1 { 217 return 218 } 219 for { 220 <-time.After(bc.dur) 221 bc.RLock() 222 if bc.items == nil { 223 bc.RUnlock() 224 return 225 } 226 bc.RUnlock() 227 if keys := bc.expiredKeys(); len(keys) != 0 { 228 bc.clearItems(keys) 229 } 230 } 231 } 232 233 // expiredKeys returns key list which are expired. 234 func (bc *MemoryCache) expiredKeys() (keys []string) { 235 bc.RLock() 236 defer bc.RUnlock() 237 for key, itm := range bc.items { 238 if itm.isExpire() { 239 keys = append(keys, key) 240 } 241 } 242 return 243 } 244 245 // clearItems removes all the items which key in keys. 246 func (bc *MemoryCache) clearItems(keys []string) { 247 bc.Lock() 248 defer bc.Unlock() 249 for _, key := range keys { 250 delete(bc.items, key) 251 } 252 } 253 254 func init() { 255 Register("memory", NewMemoryCache) 256 }