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  }