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