github.com/zhongdalu/gf@v1.0.0/g/os/gcache/gcache_mem_cache.go (about) 1 // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 7 package gcache 8 9 import ( 10 "math" 11 "sync" 12 "time" 13 14 "github.com/zhongdalu/gf/g/container/glist" 15 "github.com/zhongdalu/gf/g/container/gset" 16 "github.com/zhongdalu/gf/g/container/gtype" 17 "github.com/zhongdalu/gf/g/os/gtime" 18 "github.com/zhongdalu/gf/g/os/gtimer" 19 "github.com/zhongdalu/gf/g/util/gconv" 20 ) 21 22 // Internal cache object. 23 type memCache struct { 24 dataMu sync.RWMutex 25 expireTimeMu sync.RWMutex 26 expireSetMu sync.RWMutex 27 28 // <cap> limits the size of the cache pool. 29 // If the size of the cache exceeds the <cap>, 30 // the cache expiration process is performed according to the LRU algorithm. 31 // It is 0 in default which means no limits. 32 cap int 33 data map[interface{}]memCacheItem // Underlying cache data which is stored in a hash table. 34 expireTimes map[interface{}]int64 // Expiring key mapping to its timestamp, which is used for quick indexing and deleting. 35 expireSets map[int64]*gset.Set // Expiring timestamp mapping to its key set, which is used for quick indexing and deleting. 36 37 lru *memCacheLru // LRU object, which is enabled when <cap> > 0. 38 lruGetList *glist.List // LRU history according with Get function. 39 eventList *glist.List // Asynchronous event list for internal data synchronization. 40 closed *gtype.Bool // Is this cache closed or not. 41 } 42 43 // Internal cache item. 44 type memCacheItem struct { 45 v interface{} // Value. 46 e int64 // Expire time in milliseconds. 47 } 48 49 // Internal event item. 50 type memCacheEvent struct { 51 k interface{} // Key. 52 e int64 // Expire time in milliseconds. 53 } 54 55 const ( 56 // Default expire time for no expiring items. 57 // It equals to math.MaxInt64/1000000. 58 gDEFAULT_MAX_EXPIRE = 9223372036854 59 ) 60 61 // newMemCache creates and returns a new memory cache object. 62 func newMemCache(lruCap ...int) *memCache { 63 c := &memCache{ 64 lruGetList: glist.New(), 65 data: make(map[interface{}]memCacheItem), 66 expireTimes: make(map[interface{}]int64), 67 expireSets: make(map[int64]*gset.Set), 68 eventList: glist.New(), 69 closed: gtype.NewBool(), 70 } 71 if len(lruCap) > 0 { 72 c.cap = lruCap[0] 73 c.lru = newMemCacheLru(c) 74 } 75 return c 76 } 77 78 // makeExpireKey groups the <expire> in milliseconds to its according seconds. 79 func (c *memCache) makeExpireKey(expire int64) int64 { 80 return int64(math.Ceil(float64(expire/1000)+1) * 1000) 81 } 82 83 // getExpireSet returns the expire set for given <expire> in seconds. 84 func (c *memCache) getExpireSet(expire int64) (expireSet *gset.Set) { 85 c.expireSetMu.RLock() 86 expireSet, _ = c.expireSets[expire] 87 c.expireSetMu.RUnlock() 88 return 89 } 90 91 // getOrNewExpireSet returns the expire set for given <expire> in seconds. 92 // It creates and returns a new set for <expire> if it does not exist. 93 func (c *memCache) getOrNewExpireSet(expire int64) (expireSet *gset.Set) { 94 if expireSet = c.getExpireSet(expire); expireSet == nil { 95 expireSet = gset.New() 96 c.expireSetMu.Lock() 97 if es, ok := c.expireSets[expire]; ok { 98 expireSet = es 99 } else { 100 c.expireSets[expire] = expireSet 101 } 102 c.expireSetMu.Unlock() 103 } 104 return 105 } 106 107 // getMilliExpire converts parameter <duration> to int type in milliseconds. 108 // 109 // Note that there's some performance cost in type assertion here, but it's valuable. 110 func (c *memCache) getMilliExpire(duration interface{}) int { 111 if d, ok := duration.(time.Duration); ok { 112 return int(d.Nanoseconds() / 1000000) 113 } else { 114 return duration.(int) 115 } 116 } 117 118 // Set sets cache with <key>-<value> pair, which is expired after <duration>. 119 // 120 // The parameter <duration> can be either type of int or time.Duration. 121 // If <duration> is type of int, it means <duration> milliseconds. 122 // If <duration> <=0 means it does not expire. 123 func (c *memCache) Set(key interface{}, value interface{}, duration interface{}) { 124 expire := c.getMilliExpire(duration) 125 expireTime := c.getInternalExpire(expire) 126 c.dataMu.Lock() 127 c.data[key] = memCacheItem{v: value, e: expireTime} 128 c.dataMu.Unlock() 129 c.eventList.PushBack(&memCacheEvent{k: key, e: expireTime}) 130 } 131 132 // doSetWithLockCheck sets cache with <key>-<value> pair if <key> does not exist in the cache, 133 // which is expired after <duration>. 134 // 135 // The parameter <duration> can be either type of int or time.Duration. 136 // If <duration> is type of int, it means <duration> milliseconds. 137 // If <duration> <=0 means it does not expire. 138 // 139 // It doubly checks the <key> whether exists in the cache using mutex writing lock 140 // before setting it to the cache. 141 func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, duration interface{}) interface{} { 142 expire := c.getMilliExpire(duration) 143 expireTimestamp := c.getInternalExpire(expire) 144 c.dataMu.Lock() 145 defer c.dataMu.Unlock() 146 if v, ok := c.data[key]; ok && !v.IsExpired() { 147 return v.v 148 } 149 if f, ok := value.(func() interface{}); ok { 150 value = f() 151 } 152 if value == nil { 153 return nil 154 } 155 c.data[key] = memCacheItem{v: value, e: expireTimestamp} 156 c.eventList.PushBack(&memCacheEvent{k: key, e: expireTimestamp}) 157 return value 158 } 159 160 // getInternalExpire returns the expire time with given expire duration in milliseconds. 161 func (c *memCache) getInternalExpire(expire int) int64 { 162 if expire != 0 { 163 return gtime.Millisecond() + int64(expire) 164 } else { 165 return gDEFAULT_MAX_EXPIRE 166 } 167 } 168 169 // SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache, 170 // which is expired after <duration>. 171 // 172 // The parameter <duration> can be either type of int or time.Duration. 173 // If <duration> is type of int, it means <duration> milliseconds. 174 // If <duration> <=0 means it does not expire. 175 func (c *memCache) SetIfNotExist(key interface{}, value interface{}, duration interface{}) bool { 176 expire := c.getMilliExpire(duration) 177 if !c.Contains(key) { 178 c.doSetWithLockCheck(key, value, expire) 179 return true 180 } 181 return false 182 } 183 184 // Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>. 185 // 186 // The parameter <duration> can be either type of int or time.Duration. 187 // If <duration> is type of int, it means <duration> milliseconds. 188 // If <duration> <=0 means it does not expire. 189 func (c *memCache) Sets(data map[interface{}]interface{}, duration interface{}) { 190 expire := c.getMilliExpire(duration) 191 expireTime := c.getInternalExpire(expire) 192 for k, v := range data { 193 c.dataMu.Lock() 194 c.data[k] = memCacheItem{v: v, e: expireTime} 195 c.dataMu.Unlock() 196 c.eventList.PushBack(&memCacheEvent{k: k, e: expireTime}) 197 } 198 } 199 200 // Get returns the value of <key>. 201 // It returns nil if it does not exist or its value is nil. 202 func (c *memCache) Get(key interface{}) interface{} { 203 c.dataMu.RLock() 204 item, ok := c.data[key] 205 c.dataMu.RUnlock() 206 if ok && !item.IsExpired() { 207 // Adding to LRU history if LRU feature is enbaled. 208 if c.cap > 0 { 209 c.lruGetList.PushBack(key) 210 } 211 return item.v 212 } 213 return nil 214 } 215 216 // GetOrSet returns the value of <key>, 217 // or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache. 218 // The key-value pair expires after <duration>. 219 // 220 // The parameter <duration> can be either type of int or time.Duration. 221 // If <duration> is type of int, it means <duration> milliseconds. 222 // If <duration> <=0 means it does not expire. 223 func (c *memCache) GetOrSet(key interface{}, value interface{}, duration interface{}) interface{} { 224 if v := c.Get(key); v == nil { 225 return c.doSetWithLockCheck(key, value, duration) 226 } else { 227 return v 228 } 229 } 230 231 // GetOrSetFunc returns the value of <key>, 232 // or sets <key> with result of function <f> and returns its result 233 // if <key> does not exist in the cache. 234 // The key-value pair expires after <duration>. 235 // 236 // The parameter <duration> can be either type of int or time.Duration. 237 // If <duration> is type of int, it means <duration> milliseconds. 238 // If <duration> <=0 means it does not expire. 239 func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, duration interface{}) interface{} { 240 if v := c.Get(key); v == nil { 241 return c.doSetWithLockCheck(key, f(), duration) 242 } else { 243 return v 244 } 245 } 246 247 // GetOrSetFuncLock returns the value of <key>, 248 // or sets <key> with result of function <f> and returns its result 249 // if <key> does not exist in the cache. 250 // The key-value pair expires after <duration>. 251 // 252 // The parameter <duration> can be either type of int or time.Duration. 253 // If <duration> is type of int, it means <duration> milliseconds. 254 // If <duration> <=0 means it does not expire. 255 // 256 // Note that the function <f> is executed within writing mutex lock. 257 func (c *memCache) GetOrSetFuncLock(key interface{}, f func() interface{}, duration interface{}) interface{} { 258 if v := c.Get(key); v == nil { 259 return c.doSetWithLockCheck(key, f, duration) 260 } else { 261 return v 262 } 263 } 264 265 // Contains returns true if <key> exists in the cache, or else returns false. 266 func (c *memCache) Contains(key interface{}) bool { 267 return c.Get(key) != nil 268 } 269 270 // Remove deletes the <key> in the cache, and returns its value. 271 func (c *memCache) Remove(key interface{}) (value interface{}) { 272 c.dataMu.RLock() 273 item, ok := c.data[key] 274 c.dataMu.RUnlock() 275 if ok { 276 value = item.v 277 c.dataMu.Lock() 278 delete(c.data, key) 279 c.dataMu.Unlock() 280 c.eventList.PushBack(&memCacheEvent{k: key, e: gtime.Millisecond() - 1000}) 281 } 282 return 283 } 284 285 // Removes deletes <keys> in the cache. 286 func (c *memCache) Removes(keys []interface{}) { 287 for _, key := range keys { 288 c.Remove(key) 289 } 290 } 291 292 // Data returns a copy of all key-value pairs in the cache as map type. 293 func (c *memCache) Data() map[interface{}]interface{} { 294 m := make(map[interface{}]interface{}) 295 c.dataMu.RLock() 296 for k, v := range c.data { 297 if !v.IsExpired() { 298 m[k] = v.v 299 } 300 } 301 c.dataMu.RUnlock() 302 return m 303 } 304 305 // Keys returns all keys in the cache as slice. 306 func (c *memCache) Keys() []interface{} { 307 keys := make([]interface{}, 0) 308 c.dataMu.RLock() 309 for k, v := range c.data { 310 if !v.IsExpired() { 311 keys = append(keys, k) 312 } 313 } 314 c.dataMu.RUnlock() 315 return keys 316 } 317 318 // KeyStrings returns all keys in the cache as string slice. 319 func (c *memCache) KeyStrings() []string { 320 return gconv.Strings(c.Keys()) 321 } 322 323 // Values returns all values in the cache as slice. 324 func (c *memCache) Values() []interface{} { 325 values := make([]interface{}, 0) 326 c.dataMu.RLock() 327 for _, v := range c.data { 328 if !v.IsExpired() { 329 values = append(values, v.v) 330 } 331 } 332 c.dataMu.RUnlock() 333 return values 334 } 335 336 // Size returns the size of the cache. 337 func (c *memCache) Size() (size int) { 338 c.dataMu.RLock() 339 size = len(c.data) 340 c.dataMu.RUnlock() 341 return 342 } 343 344 // Close closes the cache. 345 func (c *memCache) Close() { 346 if c.cap > 0 { 347 c.lru.Close() 348 } 349 c.closed.Set(true) 350 } 351 352 // Asynchronous task loop: 353 // 1. asynchronously process the data in the event list, 354 // and synchronize the results to the <expireTimes> and <expireSets> properties. 355 // 2. clean up the expired key-value pair data. 356 func (c *memCache) syncEventAndClearExpired() { 357 event := (*memCacheEvent)(nil) 358 oldExpireTime := int64(0) 359 newExpireTime := int64(0) 360 if c.closed.Val() { 361 gtimer.Exit() 362 return 363 } 364 // ======================== 365 // Data Synchronization. 366 // ======================== 367 for { 368 v := c.eventList.PopFront() 369 if v == nil { 370 break 371 } 372 event = v.(*memCacheEvent) 373 // Fetching the old expire set. 374 c.expireTimeMu.RLock() 375 oldExpireTime = c.expireTimes[event.k] 376 c.expireTimeMu.RUnlock() 377 // Calculating the new expire set. 378 newExpireTime = c.makeExpireKey(event.e) 379 if newExpireTime != oldExpireTime { 380 c.getOrNewExpireSet(newExpireTime).Add(event.k) 381 if oldExpireTime != 0 { 382 c.getOrNewExpireSet(oldExpireTime).Remove(event.k) 383 } 384 // Updating the expire time for <event.k>. 385 c.expireTimeMu.Lock() 386 c.expireTimes[event.k] = newExpireTime 387 c.expireTimeMu.Unlock() 388 } 389 // Adding the key the LRU history by writing operations. 390 if c.cap > 0 { 391 c.lru.Push(event.k) 392 } 393 } 394 // Processing expired keys from LRU. 395 if c.cap > 0 && c.lruGetList.Len() > 0 { 396 for { 397 if v := c.lruGetList.PopFront(); v != nil { 398 c.lru.Push(v) 399 } else { 400 break 401 } 402 } 403 } 404 // ======================== 405 // Data Cleaning up. 406 // ======================== 407 ek := c.makeExpireKey(gtime.Millisecond()) 408 eks := []int64{ek - 1000, ek - 2000, ek - 3000, ek - 4000, ek - 5000} 409 for _, expireTime := range eks { 410 if expireSet := c.getExpireSet(expireTime); expireSet != nil { 411 // Iterating the set to delete all keys in it. 412 expireSet.Iterator(func(key interface{}) bool { 413 c.clearByKey(key) 414 return true 415 }) 416 // Deleting the set after all of its keys are deleted. 417 c.expireSetMu.Lock() 418 delete(c.expireSets, expireTime) 419 c.expireSetMu.Unlock() 420 } 421 } 422 } 423 424 // clearByKey deletes the key-value pair with given <key>. 425 // The parameter <force> specifies whether doing this deleting forcely. 426 func (c *memCache) clearByKey(key interface{}, force ...bool) { 427 c.dataMu.Lock() 428 // Doubly check before really deleting it from cache. 429 if item, ok := c.data[key]; (ok && item.IsExpired()) || (len(force) > 0 && force[0]) { 430 delete(c.data, key) 431 } 432 c.dataMu.Unlock() 433 434 // Deleting its expire time from <expireTimes>. 435 c.expireTimeMu.Lock() 436 delete(c.expireTimes, key) 437 c.expireTimeMu.Unlock() 438 439 // Deleting it from LRU. 440 if c.cap > 0 { 441 c.lru.Remove(key) 442 } 443 }