diff --git a/os/gcache/gcache_adapter.go b/os/gcache/gcache_adapter.go index 71c22c7c9..03b9f63bc 100644 --- a/os/gcache/gcache_adapter.go +++ b/os/gcache/gcache_adapter.go @@ -36,7 +36,7 @@ type Adapter interface { SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) // Get retrieves and returns the associated value of given . - // It returns nil if it does not exist or its value is nil. + // It returns nil if it does not exist, its value is nil or it's expired. Get(key interface{}) (interface{}, error) // GetOrSet retrieves and returns the value of , or sets - pair and diff --git a/os/gcache/gcache_adapter_memory.go b/os/gcache/gcache_adapter_memory.go index 2de6328f6..5bad1bb3f 100644 --- a/os/gcache/gcache_adapter_memory.go +++ b/os/gcache/gcache_adapter_memory.go @@ -8,7 +8,6 @@ package gcache import ( "math" - "sync" "time" "github.com/gogf/gf/container/glist" @@ -20,43 +19,18 @@ import ( // Internal cache object. type adapterMemory struct { - // dataMu ensures the concurrent safety of underlying data map. - dataMu sync.RWMutex - - // expireTimeMu ensures the concurrent safety of expireTimes map. - expireTimeMu sync.RWMutex - - // expireSetMu ensures the concurrent safety of expireSets map. - expireSetMu sync.RWMutex - // cap limits the size of the cache pool. // If the size of the cache exceeds the cap, // the cache expiration process performs according to the LRU algorithm. // It is 0 in default which means no limits. - cap int - - // data is the underlying cache data which is stored in a hash table. - data map[interface{}]adapterMemoryItem - - // expireTimes is the expiring key to its timestamp mapping, - // which is used for quick indexing and deleting. - expireTimes map[interface{}]int64 - - // expireSets is the expiring timestamp to its key set mapping, - // which is used for quick indexing and deleting. - expireSets map[int64]*gset.Set - - // lru is the LRU manager, which is enabled when attribute cap > 0. - lru *adapterMemoryLru - - // lruGetList is the LRU history according with Get function. - lruGetList *glist.List - - // eventList is the asynchronous event list for internal data synchronization. - eventList *glist.List - - // closed controls the cache closed or not. - closed *gtype.Bool + cap int + data *adapterMemoryData // data is the underlying cache data which is stored in a hash table. + expireTimes *adapterMemoryExpireTimes // expireTimes is the expiring key to its timestamp mapping, which is used for quick indexing and deleting. + expireSets *adapterMemoryExpireSets // expireSets is the expiring timestamp to its key set mapping, which is used for quick indexing and deleting. + lru *adapterMemoryLru // lru is the LRU manager, which is enabled when attribute cap > 0. + lruGetList *glist.List // lruGetList is the LRU history according with Get function. + eventList *glist.List // eventList is the asynchronous event list for internal data synchronization. + closed *gtype.Bool // closed controls the cache closed or not. } // Internal cache item. @@ -72,18 +46,18 @@ type adapterMemoryEvent struct { } const ( - // gDEFAULT_MAX_EXPIRE is the default expire time for no expiring items. + // defaultMaxExpire is the default expire time for no expiring items. // It equals to math.MaxInt64/1000000. - gDEFAULT_MAX_EXPIRE = 9223372036854 + defaultMaxExpire = 9223372036854 ) // newAdapterMemory creates and returns a new memory cache object. func newAdapterMemory(lruCap ...int) *adapterMemory { c := &adapterMemory{ + data: newAdapterMemoryData(), lruGetList: glist.New(true), - data: make(map[interface{}]adapterMemoryItem), - expireTimes: make(map[interface{}]int64), - expireSets: make(map[int64]*gset.Set), + expireTimes: newAdapterMemoryExpireTimes(), + expireSets: newAdapterMemoryExpireSets(), eventList: glist.New(true), closed: gtype.NewBool(), } @@ -100,12 +74,10 @@ func newAdapterMemory(lruCap ...int) *adapterMemory { // It deletes the if < 0. func (c *adapterMemory) Set(key interface{}, value interface{}, duration time.Duration) error { expireTime := c.getInternalExpire(duration) - c.dataMu.Lock() - c.data[key] = adapterMemoryItem{ + c.data.Set(key, adapterMemoryItem{ v: value, e: expireTime, - } - c.dataMu.Unlock() + }) c.eventList.PushBack(&adapterMemoryEvent{ k: key, e: expireTime, @@ -119,16 +91,7 @@ func (c *adapterMemory) Set(key interface{}, value interface{}, duration time.Du // It deletes the if given is nil. // It does nothing if does not exist in the cache. func (c *adapterMemory) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { - c.dataMu.Lock() - defer c.dataMu.Unlock() - if item, ok := c.data[key]; ok { - c.data[key] = adapterMemoryItem{ - v: value, - e: item.e, - } - return item.v, true, nil - } - return nil, false, nil + return c.data.Update(key, value) } // UpdateExpire updates the expiration of and returns the old expiration duration value. @@ -137,20 +100,17 @@ func (c *adapterMemory) Update(key interface{}, value interface{}) (oldValue int // It deletes the if < 0. func (c *adapterMemory) UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { newExpireTime := c.getInternalExpire(duration) - c.dataMu.Lock() - defer c.dataMu.Unlock() - if item, ok := c.data[key]; ok { - c.data[key] = adapterMemoryItem{ - v: item.v, - e: newExpireTime, - } + oldDuration, err = c.data.UpdateExpire(key, newExpireTime) + if err != nil { + return + } + if oldDuration != -1 { c.eventList.PushBack(&adapterMemoryEvent{ k: key, e: newExpireTime, }) - return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil } - return -1, nil + return } // GetExpire retrieves and returns the expiration of in the cache. @@ -158,9 +118,7 @@ func (c *adapterMemory) UpdateExpire(key interface{}, duration time.Duration) (o // It returns 0 if the does not expire. // It returns -1 if the does not exist in the cache. func (c *adapterMemory) GetExpire(key interface{}) (time.Duration, error) { - c.dataMu.RLock() - defer c.dataMu.RUnlock() - if item, ok := c.data[key]; ok { + if item, ok := c.data.Get(key); ok { return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil } return -1, nil @@ -194,14 +152,14 @@ func (c *adapterMemory) SetIfNotExist(key interface{}, value interface{}, durati // It does not expire if == 0. // It deletes the keys of if < 0 or given is nil. func (c *adapterMemory) Sets(data map[interface{}]interface{}, duration time.Duration) error { - expireTime := c.getInternalExpire(duration) - for k, v := range data { - c.dataMu.Lock() - c.data[k] = adapterMemoryItem{ - v: v, - e: expireTime, - } - c.dataMu.Unlock() + var ( + expireTime = c.getInternalExpire(duration) + err = c.data.Sets(data, expireTime) + ) + if err != nil { + return err + } + for k, _ := range data { c.eventList.PushBack(&adapterMemoryEvent{ k: k, e: expireTime, @@ -213,9 +171,7 @@ func (c *adapterMemory) Sets(data map[interface{}]interface{}, duration time.Dur // Get retrieves and returns the associated value of given . // It returns nil if it does not exist or its value is nil. func (c *adapterMemory) Get(key interface{}) (interface{}, error) { - c.dataMu.RLock() - item, ok := c.data[key] - c.dataMu.RUnlock() + item, ok := c.data.Get(key) if ok && !item.IsExpired() { // Adding to LRU history if LRU feature is enabled. if c.cap > 0 { @@ -304,76 +260,44 @@ func (c *adapterMemory) Contains(key interface{}) (bool, error) { // Remove deletes the one or more keys from cache, and returns its value. // If multiple keys are given, it returns the value of the deleted last item. func (c *adapterMemory) Remove(keys ...interface{}) (value interface{}, err error) { - c.dataMu.Lock() - defer c.dataMu.Unlock() - for _, key := range keys { - item, ok := c.data[key] - if ok { - value = item.v - delete(c.data, key) - c.eventList.PushBack(&adapterMemoryEvent{ - k: key, - e: gtime.TimestampMilli() - 1000, - }) - } + var removedKeys []interface{} + removedKeys, value, err = c.data.Remove(keys...) + if err != nil { + return } - return value, nil + for _, key := range removedKeys { + c.eventList.PushBack(&adapterMemoryEvent{ + k: key, + e: gtime.TimestampMilli() - 1000000, + }) + } + return } // Data returns a copy of all key-value pairs in the cache as map type. func (c *adapterMemory) Data() (map[interface{}]interface{}, error) { - m := make(map[interface{}]interface{}) - c.dataMu.RLock() - for k, v := range c.data { - if !v.IsExpired() { - m[k] = v.v - } - } - c.dataMu.RUnlock() - return m, nil + return c.data.Data() } // Keys returns all keys in the cache as slice. func (c *adapterMemory) Keys() ([]interface{}, error) { - keys := make([]interface{}, 0) - c.dataMu.RLock() - for k, v := range c.data { - if !v.IsExpired() { - keys = append(keys, k) - } - } - c.dataMu.RUnlock() - return keys, nil + return c.data.Keys() } // Values returns all values in the cache as slice. func (c *adapterMemory) Values() ([]interface{}, error) { - values := make([]interface{}, 0) - c.dataMu.RLock() - for _, v := range c.data { - if !v.IsExpired() { - values = append(values, v.v) - } - } - c.dataMu.RUnlock() - return values, nil + return c.data.Values() } // Size returns the size of the cache. func (c *adapterMemory) Size() (size int, err error) { - c.dataMu.RLock() - size = len(c.data) - c.dataMu.RUnlock() - return size, nil + return c.data.Size() } // Clear clears all data of the cache. // Note that this function is sensitive and should be carefully used. func (c *adapterMemory) Clear() error { - c.dataMu.Lock() - defer c.dataMu.Unlock() - c.data = make(map[interface{}]adapterMemoryItem) - return nil + return c.data.Clear() } // Close closes the cache. @@ -394,33 +318,17 @@ func (c *adapterMemory) Close() error { // // It doubly checks the whether exists in the cache using mutex writing lock // before setting it to the cache. -func (c *adapterMemory) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) (interface{}, error) { +func (c *adapterMemory) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) (result interface{}, err error) { expireTimestamp := c.getInternalExpire(duration) - c.dataMu.Lock() - defer c.dataMu.Unlock() - if v, ok := c.data[key]; ok && !v.IsExpired() { - return v.v, nil - } - if f, ok := value.(func() (interface{}, error)); ok { - v, err := f() - if err != nil { - return nil, err - } - if v == nil { - return nil, nil - } else { - value = v - } - } - c.data[key] = adapterMemoryItem{v: value, e: expireTimestamp} + result, err = c.data.SetWithLock(key, value, expireTimestamp) c.eventList.PushBack(&adapterMemoryEvent{k: key, e: expireTimestamp}) - return value, nil + return } // getInternalExpire converts and returns the expire time with given expired duration in milliseconds. func (c *adapterMemory) getInternalExpire(duration time.Duration) int64 { if duration == 0 { - return gDEFAULT_MAX_EXPIRE + return defaultMaxExpire } else { return gtime.TimestampMilli() + duration.Nanoseconds()/1000000 } @@ -431,30 +339,6 @@ func (c *adapterMemory) makeExpireKey(expire int64) int64 { return int64(math.Ceil(float64(expire/1000)+1) * 1000) } -// getExpireSet returns the expire set for given in seconds. -func (c *adapterMemory) getExpireSet(expire int64) (expireSet *gset.Set) { - c.expireSetMu.RLock() - expireSet, _ = c.expireSets[expire] - c.expireSetMu.RUnlock() - return -} - -// getOrNewExpireSet returns the expire set for given in seconds. -// It creates and returns a new set for if it does not exist. -func (c *adapterMemory) getOrNewExpireSet(expire int64) (expireSet *gset.Set) { - if expireSet = c.getExpireSet(expire); expireSet == nil { - expireSet = gset.New(true) - c.expireSetMu.Lock() - if es, ok := c.expireSets[expire]; ok { - expireSet = es - } else { - c.expireSets[expire] = expireSet - } - c.expireSetMu.Unlock() - } - return -} - // syncEventAndClearExpired does the asynchronous task loop: // 1. Asynchronously process the data in the event list, // and synchronize the results to the and properties. @@ -479,20 +363,16 @@ func (c *adapterMemory) syncEventAndClearExpired() { } event = v.(*adapterMemoryEvent) // Fetching the old expire set. - c.expireTimeMu.RLock() - oldExpireTime = c.expireTimes[event.k] - c.expireTimeMu.RUnlock() + oldExpireTime = c.expireTimes.Get(event.k) // Calculating the new expire set. newExpireTime = c.makeExpireKey(event.e) if newExpireTime != oldExpireTime { - c.getOrNewExpireSet(newExpireTime).Add(event.k) + c.expireSets.GetOrNew(newExpireTime).Add(event.k) if oldExpireTime != 0 { - c.getOrNewExpireSet(oldExpireTime).Remove(event.k) + c.expireSets.GetOrNew(oldExpireTime).Remove(event.k) } // Updating the expire time for . - c.expireTimeMu.Lock() - c.expireTimes[event.k] = newExpireTime - c.expireTimeMu.Unlock() + c.expireTimes.Set(event.k, newExpireTime) } // Adding the key the LRU history by writing operations. if c.cap > 0 { @@ -518,16 +398,14 @@ func (c *adapterMemory) syncEventAndClearExpired() { eks = []int64{ek - 1000, ek - 2000, ek - 3000, ek - 4000, ek - 5000} ) for _, expireTime := range eks { - if expireSet = c.getExpireSet(expireTime); expireSet != nil { + if expireSet = c.expireSets.Get(expireTime); expireSet != nil { // Iterating the set to delete all keys in it. expireSet.Iterator(func(key interface{}) bool { c.clearByKey(key) return true }) // Deleting the set after all of its keys are deleted. - c.expireSetMu.Lock() - delete(c.expireSets, expireTime) - c.expireSetMu.Unlock() + c.expireSets.Delete(expireTime) } } } @@ -535,17 +413,11 @@ func (c *adapterMemory) syncEventAndClearExpired() { // clearByKey deletes the key-value pair with given . // The parameter specifies whether doing this deleting forcibly. func (c *adapterMemory) clearByKey(key interface{}, force ...bool) { - c.dataMu.Lock() // Doubly check before really deleting it from cache. - if item, ok := c.data[key]; (ok && item.IsExpired()) || (len(force) > 0 && force[0]) { - delete(c.data, key) - } - c.dataMu.Unlock() + c.data.DeleteWithDoubleCheck(key, force...) // Deleting its expire time from . - c.expireTimeMu.Lock() - delete(c.expireTimes, key) - c.expireTimeMu.Unlock() + c.expireTimes.Delete(key) // Deleting it from LRU. if c.cap > 0 { diff --git a/os/gcache/gcache_adapter_memory_data.go b/os/gcache/gcache_adapter_memory_data.go new file mode 100644 index 000000000..839922e41 --- /dev/null +++ b/os/gcache/gcache_adapter_memory_data.go @@ -0,0 +1,199 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcache + +import ( + "github.com/gogf/gf/os/gtime" + "sync" + "time" +) + +type adapterMemoryData struct { + mu sync.RWMutex // dataMu ensures the concurrent safety of underlying data map. + data map[interface{}]adapterMemoryItem // data is the underlying cache data which is stored in a hash table. +} + +func newAdapterMemoryData() *adapterMemoryData { + return &adapterMemoryData{ + data: make(map[interface{}]adapterMemoryItem), + } +} + +// Update updates the value of without changing its expiration and returns the old value. +// The returned value is false if the does not exist in the cache. +// +// It deletes the if given is nil. +// It does nothing if does not exist in the cache. +func (d *adapterMemoryData) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { + d.mu.Lock() + defer d.mu.Unlock() + if item, ok := d.data[key]; ok { + d.data[key] = adapterMemoryItem{ + v: value, + e: item.e, + } + return item.v, true, nil + } + return nil, false, nil +} + +// UpdateExpire updates the expiration of and returns the old expiration duration value. +// +// It returns -1 and does nothing if the does not exist in the cache. +// It deletes the if < 0. +func (d *adapterMemoryData) UpdateExpire(key interface{}, expireTime int64) (oldDuration time.Duration, err error) { + d.mu.Lock() + defer d.mu.Unlock() + if item, ok := d.data[key]; ok { + d.data[key] = adapterMemoryItem{ + v: item.v, + e: expireTime, + } + return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil + } + return -1, nil +} + +// Remove deletes the one or more keys from cache, and returns its value. +// If multiple keys are given, it returns the value of the deleted last item. +func (d *adapterMemoryData) Remove(keys ...interface{}) (removedKeys []interface{}, value interface{}, err error) { + d.mu.Lock() + defer d.mu.Unlock() + removedKeys = make([]interface{}, 0) + for _, key := range keys { + item, ok := d.data[key] + if ok { + value = item.v + delete(d.data, key) + removedKeys = append(removedKeys, key) + } + } + return removedKeys, value, nil +} + +// Data returns a copy of all key-value pairs in the cache as map type. +func (d *adapterMemoryData) Data() (map[interface{}]interface{}, error) { + d.mu.RLock() + m := make(map[interface{}]interface{}, len(d.data)) + for k, v := range d.data { + if !v.IsExpired() { + m[k] = v.v + } + } + d.mu.RUnlock() + return m, nil +} + +// Keys returns all keys in the cache as slice. +func (d *adapterMemoryData) Keys() ([]interface{}, error) { + d.mu.RLock() + var ( + index = 0 + keys = make([]interface{}, len(d.data)) + ) + for k, v := range d.data { + if !v.IsExpired() { + keys[index] = k + index++ + } + } + d.mu.RUnlock() + return keys, nil +} + +// Values returns all values in the cache as slice. +func (d *adapterMemoryData) Values() ([]interface{}, error) { + d.mu.RLock() + var ( + index = 0 + values = make([]interface{}, len(d.data)) + ) + for _, v := range d.data { + if !v.IsExpired() { + values[index] = v.v + index++ + } + } + d.mu.RUnlock() + return values, nil +} + +// Size returns the size of the cache. +func (d *adapterMemoryData) Size() (size int, err error) { + d.mu.RLock() + size = len(d.data) + d.mu.RUnlock() + return size, nil +} + +// Clear clears all data of the cache. +// Note that this function is sensitive and should be carefully used. +func (d *adapterMemoryData) Clear() error { + d.mu.Lock() + defer d.mu.Unlock() + d.data = make(map[interface{}]adapterMemoryItem) + return nil +} + +func (d *adapterMemoryData) Get(key interface{}) (item adapterMemoryItem, ok bool) { + d.mu.RLock() + item, ok = d.data[key] + d.mu.RUnlock() + return +} + +func (d *adapterMemoryData) Set(key interface{}, value adapterMemoryItem) { + d.mu.Lock() + d.data[key] = value + d.mu.Unlock() +} + +// Sets batch sets cache with key-value pairs by , which is expired after . +// +// It does not expire if == 0. +// It deletes the keys of if < 0 or given is nil. +func (d *adapterMemoryData) Sets(data map[interface{}]interface{}, expireTime int64) error { + d.mu.Lock() + for k, v := range data { + d.data[k] = adapterMemoryItem{ + v: v, + e: expireTime, + } + } + d.mu.Unlock() + return nil +} + +func (d *adapterMemoryData) SetWithLock(key interface{}, value interface{}, expireTimestamp int64) (interface{}, error) { + d.mu.Lock() + defer d.mu.Unlock() + if v, ok := d.data[key]; ok && !v.IsExpired() { + return v.v, nil + } + if f, ok := value.(func() (interface{}, error)); ok { + v, err := f() + if err != nil { + return nil, err + } + if v == nil { + return nil, nil + } else { + value = v + } + } + d.data[key] = adapterMemoryItem{v: value, e: expireTimestamp} + return value, nil +} + +func (d *adapterMemoryData) DeleteWithDoubleCheck(key interface{}, force ...bool) { + d.mu.Lock() + // Doubly check before really deleting it from cache. + if item, ok := d.data[key]; (ok && item.IsExpired()) || (len(force) > 0 && force[0]) { + delete(d.data, key) + } + d.mu.Unlock() +} diff --git a/os/gcache/gcache_adapter_memory_expire_sets.go b/os/gcache/gcache_adapter_memory_expire_sets.go new file mode 100644 index 000000000..f041f65ac --- /dev/null +++ b/os/gcache/gcache_adapter_memory_expire_sets.go @@ -0,0 +1,51 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcache + +import ( + "github.com/gogf/gf/container/gset" + "sync" +) + +type adapterMemoryExpireSets struct { + mu sync.RWMutex // expireSetMu ensures the concurrent safety of expireSets map. + expireSets map[int64]*gset.Set // expireSets is the expiring timestamp to its key set mapping, which is used for quick indexing and deleting. +} + +func newAdapterMemoryExpireSets() *adapterMemoryExpireSets { + return &adapterMemoryExpireSets{ + expireSets: make(map[int64]*gset.Set), + } +} + +func (d *adapterMemoryExpireSets) Get(key int64) (result *gset.Set) { + d.mu.RLock() + result = d.expireSets[key] + d.mu.RUnlock() + return +} + +func (d *adapterMemoryExpireSets) GetOrNew(key int64) (result *gset.Set) { + if result = d.Get(key); result != nil { + return + } + d.mu.Lock() + if es, ok := d.expireSets[key]; ok { + result = es + } else { + result = gset.New(true) + d.expireSets[key] = result + } + d.mu.Unlock() + return +} + +func (d *adapterMemoryExpireSets) Delete(key int64) { + d.mu.Lock() + delete(d.expireSets, key) + d.mu.Unlock() +} diff --git a/os/gcache/gcache_adapter_memory_expire_times.go b/os/gcache/gcache_adapter_memory_expire_times.go new file mode 100644 index 000000000..af3d4b419 --- /dev/null +++ b/os/gcache/gcache_adapter_memory_expire_times.go @@ -0,0 +1,41 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcache + +import ( + "sync" +) + +type adapterMemoryExpireTimes struct { + mu sync.RWMutex // expireTimeMu ensures the concurrent safety of expireTimes map. + expireTimes map[interface{}]int64 // expireTimes is the expiring key to its timestamp mapping, which is used for quick indexing and deleting. +} + +func newAdapterMemoryExpireTimes() *adapterMemoryExpireTimes { + return &adapterMemoryExpireTimes{ + expireTimes: make(map[interface{}]int64), + } +} + +func (d *adapterMemoryExpireTimes) Get(key interface{}) (value int64) { + d.mu.RLock() + value = d.expireTimes[key] + d.mu.RUnlock() + return +} + +func (d *adapterMemoryExpireTimes) Set(key interface{}, value int64) { + d.mu.Lock() + d.expireTimes[key] = value + d.mu.Unlock() +} + +func (d *adapterMemoryExpireTimes) Delete(key interface{}) { + d.mu.Lock() + delete(d.expireTimes, key) + d.mu.Unlock() +}