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  }