mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
improve package gcache
This commit is contained in:
@ -13,20 +13,20 @@ import "time"
|
||||
var cache = New()
|
||||
|
||||
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
|
||||
// It does not expire if <duration> <= 0.
|
||||
// It does not expire if <duration> == 0.
|
||||
func Set(key interface{}, value interface{}, duration time.Duration) {
|
||||
cache.Set(key, value, duration)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
|
||||
// which is expired after <duration>. It does not expire if <duration> <= 0.
|
||||
// which is expired after <duration>. It does not expire if <duration> == 0.
|
||||
func SetIfNotExist(key interface{}, value interface{}, duration time.Duration) bool {
|
||||
return cache.SetIfNotExist(key, value, duration)
|
||||
}
|
||||
|
||||
// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>.
|
||||
//
|
||||
// It does not expire if <duration> <= 0.
|
||||
// It does not expire if <duration> == 0.
|
||||
func Sets(data map[interface{}]interface{}, duration time.Duration) {
|
||||
cache.Sets(data, duration)
|
||||
}
|
||||
@ -41,21 +41,21 @@ func Get(key interface{}) interface{} {
|
||||
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
|
||||
// The key-value pair expires after <duration>.
|
||||
//
|
||||
// It does not expire if <duration> <= 0.
|
||||
// It does not expire if <duration> == 0.
|
||||
func GetOrSet(key interface{}, value interface{}, duration time.Duration) interface{} {
|
||||
return cache.GetOrSet(key, value, duration)
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value of <key>, or sets <key> with result of function <f>
|
||||
// and returns its result if <key> does not exist in the cache. The key-value pair expires
|
||||
// after <duration>. It does not expire if <duration> <= 0.
|
||||
// after <duration>. It does not expire if <duration> == 0.
|
||||
func GetOrSetFunc(key interface{}, f func() interface{}, duration time.Duration) interface{} {
|
||||
return cache.GetOrSetFunc(key, f, duration)
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value of <key>, or sets <key> with result of function <f>
|
||||
// and returns its result if <key> does not exist in the cache. The key-value pair expires
|
||||
// after <duration>. It does not expire if <duration> <= 0.
|
||||
// after <duration>. It does not expire if <duration> == 0.
|
||||
//
|
||||
// Note that the function <f> is executed within writing mutex lock.
|
||||
func GetOrSetFuncLock(key interface{}, f func() interface{}, duration time.Duration) interface{} {
|
||||
|
||||
@ -21,23 +21,43 @@ import (
|
||||
|
||||
// Internal cache object.
|
||||
type memCache struct {
|
||||
dataMu sync.RWMutex
|
||||
expireTimeMu sync.RWMutex
|
||||
expireSetMu sync.RWMutex
|
||||
// dataMu ensures the concurrent safety of underlying data map.
|
||||
dataMu sync.RWMutex
|
||||
|
||||
// <cap> limits the size of the cache pool.
|
||||
// If the size of the cache exceeds the <cap>,
|
||||
// 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 map[interface{}]memCacheItem // Underlying cache data which is stored in a hash table.
|
||||
expireTimes map[interface{}]int64 // Expiring key mapping to its timestamp, which is used for quick indexing and deleting.
|
||||
expireSets map[int64]*gset.Set // Expiring timestamp mapping to its key set, which is used for quick indexing and deleting.
|
||||
cap int
|
||||
|
||||
lru *memCacheLru // LRU manager, which is enabled when <cap> > 0.
|
||||
lruGetList *glist.List // LRU history according with Get function.
|
||||
eventList *glist.List // Asynchronous event list for internal data synchronization.
|
||||
closed *gtype.Bool // Is this cache closed or not.
|
||||
// data is the underlying cache data which is stored in a hash table.
|
||||
data map[interface{}]memCacheItem
|
||||
|
||||
// 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 *memCacheLru
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Internal cache item.
|
||||
@ -53,7 +73,7 @@ type memCacheEvent struct {
|
||||
}
|
||||
|
||||
const (
|
||||
// Default expire time for no expiring items.
|
||||
// gDEFAULT_MAX_EXPIRE is the default expire time for no expiring items.
|
||||
// It equals to math.MaxInt64/1000000.
|
||||
gDEFAULT_MAX_EXPIRE = 9223372036854
|
||||
)
|
||||
@ -75,6 +95,59 @@ func newMemCache(lruCap ...int) *memCache {
|
||||
return c
|
||||
}
|
||||
|
||||
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
func (c *memCache) Set(key interface{}, value interface{}, duration time.Duration) {
|
||||
expireTime := c.getInternalExpire(duration)
|
||||
c.dataMu.Lock()
|
||||
c.data[key] = memCacheItem{
|
||||
v: value,
|
||||
e: expireTime,
|
||||
}
|
||||
c.dataMu.Unlock()
|
||||
c.eventList.PushBack(&memCacheEvent{
|
||||
k: key,
|
||||
e: expireTime,
|
||||
})
|
||||
}
|
||||
|
||||
// doSetWithLockCheck sets cache with <key>-<value> pair if <key> does not exist in the
|
||||
// cache, which is expired after <duration>.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// The parameter <value> can be type of <func() interface{}>, but it dose nothing if its
|
||||
// result is nil.
|
||||
//
|
||||
// It doubly checks the <key> whether exists in the cache using mutex writing lock
|
||||
// before setting it to the cache.
|
||||
func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) interface{} {
|
||||
expireTimestamp := c.getInternalExpire(duration)
|
||||
c.dataMu.Lock()
|
||||
defer c.dataMu.Unlock()
|
||||
if v, ok := c.data[key]; ok && !v.IsExpired() {
|
||||
return v.v
|
||||
}
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
value = f()
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
c.data[key] = memCacheItem{v: value, e: expireTimestamp}
|
||||
c.eventList.PushBack(&memCacheEvent{k: key, e: expireTimestamp})
|
||||
return value
|
||||
}
|
||||
|
||||
// getInternalExpire converts and returns the expire time with given expired duration in milliseconds.
|
||||
func (c *memCache) getInternalExpire(duration time.Duration) int64 {
|
||||
if duration == 0 {
|
||||
return gDEFAULT_MAX_EXPIRE
|
||||
} else {
|
||||
return gtime.TimestampMilli() + duration.Nanoseconds()/1000000
|
||||
}
|
||||
}
|
||||
|
||||
// makeExpireKey groups the <expire> in milliseconds to its according seconds.
|
||||
func (c *memCache) makeExpireKey(expire int64) int64 {
|
||||
return int64(math.Ceil(float64(expire/1000)+1) * 1000)
|
||||
@ -104,53 +177,8 @@ func (c *memCache) getOrNewExpireSet(expire int64) (expireSet *gset.Set) {
|
||||
return
|
||||
}
|
||||
|
||||
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
|
||||
//
|
||||
// It does not expire if <duration> <= 0.
|
||||
func (c *memCache) Set(key interface{}, value interface{}, duration time.Duration) {
|
||||
expireTime := c.getInternalExpire(duration.Nanoseconds() / 1000000)
|
||||
c.dataMu.Lock()
|
||||
c.data[key] = memCacheItem{v: value, e: expireTime}
|
||||
c.dataMu.Unlock()
|
||||
c.eventList.PushBack(&memCacheEvent{k: key, e: expireTime})
|
||||
}
|
||||
|
||||
// doSetWithLockCheck sets cache with <key>-<value> pair if <key> does not exist in the cache,
|
||||
// which is expired after <duration>.
|
||||
//
|
||||
// It does not expire if <duration> <= 0.
|
||||
//
|
||||
// It doubly checks the <key> whether exists in the cache using mutex writing lock
|
||||
// before setting it to the cache.
|
||||
func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) interface{} {
|
||||
expireTimestamp := c.getInternalExpire(duration.Nanoseconds() / 1000000)
|
||||
c.dataMu.Lock()
|
||||
defer c.dataMu.Unlock()
|
||||
if v, ok := c.data[key]; ok && !v.IsExpired() {
|
||||
return v.v
|
||||
}
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
value = f()
|
||||
}
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
c.data[key] = memCacheItem{v: value, e: expireTimestamp}
|
||||
c.eventList.PushBack(&memCacheEvent{k: key, e: expireTimestamp})
|
||||
return value
|
||||
}
|
||||
|
||||
// getInternalExpire returns the expire time with given expire duration in milliseconds.
|
||||
func (c *memCache) getInternalExpire(expire int64) int64 {
|
||||
if expire <= 0 {
|
||||
return gDEFAULT_MAX_EXPIRE
|
||||
} else {
|
||||
return gtime.TimestampMilli() + expire
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
|
||||
// which is expired after <duration>. It does not expire if <duration> <= 0.
|
||||
// which is expired after <duration>. It does not expire if <duration> == 0.
|
||||
func (c *memCache) SetIfNotExist(key interface{}, value interface{}, duration time.Duration) bool {
|
||||
if !c.Contains(key) {
|
||||
c.doSetWithLockCheck(key, value, duration)
|
||||
@ -161,14 +189,20 @@ func (c *memCache) SetIfNotExist(key interface{}, value interface{}, duration ti
|
||||
|
||||
// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>.
|
||||
//
|
||||
// It does not expire if <duration> <= 0.
|
||||
// It does not expire if <duration> == 0.
|
||||
func (c *memCache) Sets(data map[interface{}]interface{}, duration time.Duration) {
|
||||
expireTime := c.getInternalExpire(duration.Nanoseconds() / 1000000)
|
||||
expireTime := c.getInternalExpire(duration)
|
||||
for k, v := range data {
|
||||
c.dataMu.Lock()
|
||||
c.data[k] = memCacheItem{v: v, e: expireTime}
|
||||
c.data[k] = memCacheItem{
|
||||
v: v,
|
||||
e: expireTime,
|
||||
}
|
||||
c.dataMu.Unlock()
|
||||
c.eventList.PushBack(&memCacheEvent{k: k, e: expireTime})
|
||||
c.eventList.PushBack(&memCacheEvent{
|
||||
k: k,
|
||||
e: expireTime,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,7 +224,7 @@ func (c *memCache) Get(key interface{}) interface{} {
|
||||
|
||||
// GetOrSet returns the value of <key>, or sets <key>-<value> pair and returns <value> if <key>
|
||||
// does not exist in the cache. The key-value pair expires after <duration>. It does not expire
|
||||
// if <duration> <= 0.
|
||||
// if <duration> == 0.
|
||||
func (c *memCache) GetOrSet(key interface{}, value interface{}, duration time.Duration) interface{} {
|
||||
if v := c.Get(key); v == nil {
|
||||
return c.doSetWithLockCheck(key, value, duration)
|
||||
@ -201,7 +235,10 @@ func (c *memCache) GetOrSet(key interface{}, value interface{}, duration time.Du
|
||||
|
||||
// GetOrSetFunc returns the value of <key>, or sets <key> with result of function <f>
|
||||
// and returns its result if <key> does not exist in the cache. The key-value pair expires
|
||||
// after <duration>. It does not expire if <duration> <= 0.
|
||||
// after <duration>.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It does nothing if function <f> returns nil.
|
||||
func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, duration time.Duration) interface{} {
|
||||
if v := c.Get(key); v == nil {
|
||||
return c.doSetWithLockCheck(key, f(), duration)
|
||||
@ -212,7 +249,10 @@ func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, duration
|
||||
|
||||
// GetOrSetFuncLock returns the value of <key>, or sets <key> with result of function <f>
|
||||
// and returns its result if <key> does not exist in the cache. The key-value pair expires
|
||||
// after <duration>. It does not expire if <duration> <= 0.
|
||||
// after <duration>.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It does nothing if function <f> returns nil.
|
||||
//
|
||||
// Note that the function <f> is executed within writing mutex lock.
|
||||
func (c *memCache) GetOrSetFuncLock(key interface{}, f func() interface{}, duration time.Duration) interface{} {
|
||||
@ -238,7 +278,10 @@ func (c *memCache) Remove(key interface{}) (value interface{}) {
|
||||
c.dataMu.Lock()
|
||||
delete(c.data, key)
|
||||
c.dataMu.Unlock()
|
||||
c.eventList.PushBack(&memCacheEvent{k: key, e: gtime.TimestampMilli() - 1000})
|
||||
c.eventList.PushBack(&memCacheEvent{
|
||||
k: key,
|
||||
e: gtime.TimestampMilli() - 1000,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -310,18 +353,20 @@ func (c *memCache) Close() {
|
||||
c.closed.Set(true)
|
||||
}
|
||||
|
||||
// Asynchronous task loop:
|
||||
// 1. asynchronously process the data in the event list,
|
||||
// syncEventAndClearExpired does the asynchronous task loop:
|
||||
// 1. Asynchronously process the data in the event list,
|
||||
// and synchronize the results to the <expireTimes> and <expireSets> properties.
|
||||
// 2. clean up the expired key-value pair data.
|
||||
// 2. Clean up the expired key-value pair data.
|
||||
func (c *memCache) syncEventAndClearExpired() {
|
||||
event := (*memCacheEvent)(nil)
|
||||
oldExpireTime := int64(0)
|
||||
newExpireTime := int64(0)
|
||||
if c.closed.Val() {
|
||||
gtimer.Exit()
|
||||
return
|
||||
}
|
||||
var (
|
||||
event *memCacheEvent
|
||||
oldExpireTime int64
|
||||
newExpireTime int64
|
||||
)
|
||||
// ========================
|
||||
// Data Synchronization.
|
||||
// ========================
|
||||
@ -365,10 +410,13 @@ func (c *memCache) syncEventAndClearExpired() {
|
||||
// ========================
|
||||
// Data Cleaning up.
|
||||
// ========================
|
||||
ek := c.makeExpireKey(gtime.TimestampMilli())
|
||||
eks := []int64{ek - 1000, ek - 2000, ek - 3000, ek - 4000, ek - 5000}
|
||||
var (
|
||||
expireSet *gset.Set
|
||||
ek = c.makeExpireKey(gtime.TimestampMilli())
|
||||
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.getExpireSet(expireTime); expireSet != nil {
|
||||
// Iterating the set to delete all keys in it.
|
||||
expireSet.Iterator(func(key interface{}) bool {
|
||||
c.clearByKey(key)
|
||||
|
||||
Reference in New Issue
Block a user