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