improve package gcache

This commit is contained in:
jianchenma 2021-01-26 14:11:36 +08:00
parent 796774efe4
commit c01b520ba6
6 changed files with 241 additions and 44 deletions

View File

@ -9,6 +9,7 @@
package gcache
import (
"context"
"github.com/gogf/gf/container/gvar"
"time"
)
@ -16,6 +17,12 @@ import (
// Default cache object.
var defaultCache = New()
// Ctx is a chaining function, which shallowly clones current object and sets the context
// for next operation.
func Ctx(ctx context.Context) *Cache {
return defaultCache.Ctx(ctx)
}
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
// It does not expire if <duration> == 0.
func Set(key interface{}, value interface{}, duration time.Duration) {

View File

@ -7,6 +7,7 @@
package gcache
import (
"context"
"time"
)
@ -16,13 +17,13 @@ type Adapter interface {
//
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0.
Set(key interface{}, value interface{}, duration time.Duration) error
Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error
// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>.
//
// It does not expire if <duration> == 0.
// It deletes the keys of <data> if <duration> < 0 or given <value> is nil.
Sets(data map[interface{}]interface{}, duration time.Duration) error
Sets(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error
// SetIfNotExist sets cache with <key>-<value> pair which is expired after <duration>
// if <key> does not exist in the cache. It returns true the <key> dose not exist in the
@ -33,11 +34,11 @@ type Adapter interface {
//
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0 or given <value> is nil.
SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error)
SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (bool, error)
// Get retrieves and returns the associated value of given <key>.
// It returns nil if it does not exist, its value is nil or it's expired.
Get(key interface{}) (interface{}, error)
Get(ctx context.Context, key interface{}) (interface{}, error)
// GetOrSet retrieves and 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
@ -46,7 +47,7 @@ type Adapter interface {
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
// if <value> is a function and the function result is nil.
GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error)
GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (interface{}, error)
// GetOrSetFunc retrieves and 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
@ -55,7 +56,7 @@ type Adapter interface {
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
// if <value> is a function and the function result is nil.
GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error)
GetOrSetFunc(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error)
// GetOrSetFuncLock retrieves and 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
@ -66,52 +67,52 @@ type Adapter interface {
//
// Note that the function <f> should be executed within writing mutex lock for concurrent
// safety purpose.
GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error)
GetOrSetFuncLock(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error)
// Contains returns true if <key> exists in the cache, or else returns false.
Contains(key interface{}) (bool, error)
Contains(ctx context.Context, key interface{}) (bool, error)
// GetExpire retrieves and returns the expiration of <key> in the cache.
//
// It returns 0 if the <key> does not expire.
// It returns -1 if the <key> does not exist in the cache.
GetExpire(key interface{}) (time.Duration, error)
GetExpire(ctx context.Context, key interface{}) (time.Duration, error)
// Remove deletes one or more keys from cache, and returns its value.
// If multiple keys are given, it returns the value of the last deleted item.
Remove(keys ...interface{}) (value interface{}, err error)
Remove(ctx context.Context, keys ...interface{}) (value interface{}, err error)
// Update updates the value of <key> without changing its expiration and returns the old value.
// The returned value <exist> is false if the <key> does not exist in the cache.
//
// It deletes the <key> if given <value> is nil.
// It does nothing if <key> does not exist in the cache.
Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error)
Update(ctx context.Context, key interface{}, value interface{}) (oldValue interface{}, exist bool, err error)
// UpdateExpire updates the expiration of <key> and returns the old expiration duration value.
//
// It returns -1 and does nothing if the <key> does not exist in the cache.
// It deletes the <key> if <duration> < 0.
UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error)
UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error)
// Size returns the number of items in the cache.
Size() (size int, err error)
Size(ctx context.Context) (size int, err error)
// Data returns a copy of all key-value pairs in the cache as map type.
// Note that this function may leads lots of memory usage, you can implement this function
// if necessary.
Data() (map[interface{}]interface{}, error)
Data(ctx context.Context) (map[interface{}]interface{}, error)
// Keys returns all keys in the cache as slice.
Keys() ([]interface{}, error)
Keys(ctx context.Context) ([]interface{}, error)
// Values returns all values in the cache as slice.
Values() ([]interface{}, error)
Values(ctx context.Context) ([]interface{}, error)
// Clear clears all data of the cache.
// Note that this function is sensitive and should be carefully used.
Clear() error
Clear(ctx context.Context) error
// Close closes the cache if necessary.
Close() error
Close(ctx context.Context) error
}

View File

@ -7,6 +7,7 @@
package gcache
import (
"context"
"math"
"time"
@ -72,7 +73,7 @@ func newAdapterMemory(lruCap ...int) *adapterMemory {
//
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0.
func (c *adapterMemory) Set(key interface{}, value interface{}, duration time.Duration) error {
func (c *adapterMemory) Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error {
expireTime := c.getInternalExpire(duration)
c.data.Set(key, adapterMemoryItem{
v: value,
@ -90,7 +91,7 @@ func (c *adapterMemory) Set(key interface{}, value interface{}, duration time.Du
//
// It deletes the <key> if given <value> is nil.
// It does nothing if <key> does not exist in the cache.
func (c *adapterMemory) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) {
func (c *adapterMemory) Update(ctx context.Context, key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) {
return c.data.Update(key, value)
}
@ -98,7 +99,7 @@ func (c *adapterMemory) Update(key interface{}, value interface{}) (oldValue int
//
// It returns -1 and does nothing if the <key> does not exist in the cache.
// It deletes the <key> if <duration> < 0.
func (c *adapterMemory) UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) {
func (c *adapterMemory) UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) {
newExpireTime := c.getInternalExpire(duration)
oldDuration, err = c.data.UpdateExpire(key, newExpireTime)
if err != nil {
@ -117,7 +118,7 @@ func (c *adapterMemory) UpdateExpire(key interface{}, duration time.Duration) (o
//
// It returns 0 if the <key> does not expire.
// It returns -1 if the <key> does not exist in the cache.
func (c *adapterMemory) GetExpire(key interface{}) (time.Duration, error) {
func (c *adapterMemory) GetExpire(ctx context.Context, key interface{}) (time.Duration, error) {
if item, ok := c.data.Get(key); ok {
return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil
}
@ -132,8 +133,8 @@ func (c *adapterMemory) GetExpire(key interface{}) (time.Duration, error) {
//
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0 or given <value> is nil.
func (c *adapterMemory) SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) {
isContained, err := c.Contains(key)
func (c *adapterMemory) SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (bool, error) {
isContained, err := c.Contains(ctx, key)
if err != nil {
return false, err
}
@ -151,7 +152,7 @@ func (c *adapterMemory) SetIfNotExist(key interface{}, value interface{}, durati
//
// It does not expire if <duration> == 0.
// It deletes the keys of <data> if <duration> < 0 or given <value> is nil.
func (c *adapterMemory) Sets(data map[interface{}]interface{}, duration time.Duration) error {
func (c *adapterMemory) Sets(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error {
var (
expireTime = c.getInternalExpire(duration)
err = c.data.Sets(data, expireTime)
@ -170,7 +171,7 @@ func (c *adapterMemory) Sets(data map[interface{}]interface{}, duration time.Dur
// Get retrieves and returns the associated value of given <key>.
// It returns nil if it does not exist or its value is nil.
func (c *adapterMemory) Get(key interface{}) (interface{}, error) {
func (c *adapterMemory) Get(ctx context.Context, key interface{}) (interface{}, error) {
item, ok := c.data.Get(key)
if ok && !item.IsExpired() {
// Adding to LRU history if LRU feature is enabled.
@ -189,8 +190,8 @@ func (c *adapterMemory) Get(key interface{}) (interface{}, error) {
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
// if <value> is a function and the function result is nil.
func (c *adapterMemory) GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) {
v, err := c.Get(key)
func (c *adapterMemory) GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (interface{}, error) {
v, err := c.Get(ctx, key)
if err != nil {
return nil, err
}
@ -208,8 +209,8 @@ func (c *adapterMemory) GetOrSet(key interface{}, value interface{}, duration ti
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
// if <value> is a function and the function result is nil.
func (c *adapterMemory) GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
v, err := c.Get(key)
func (c *adapterMemory) GetOrSetFunc(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
v, err := c.Get(ctx, key)
if err != nil {
return nil, err
}
@ -236,8 +237,8 @@ func (c *adapterMemory) GetOrSetFunc(key interface{}, f func() (interface{}, err
//
// Note that the function <f> should be executed within writing mutex lock for concurrent
// safety purpose.
func (c *adapterMemory) GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
v, err := c.Get(key)
func (c *adapterMemory) GetOrSetFuncLock(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
v, err := c.Get(ctx, key)
if err != nil {
return nil, err
}
@ -249,8 +250,8 @@ func (c *adapterMemory) GetOrSetFuncLock(key interface{}, f func() (interface{},
}
// Contains returns true if <key> exists in the cache, or else returns false.
func (c *adapterMemory) Contains(key interface{}) (bool, error) {
v, err := c.Get(key)
func (c *adapterMemory) Contains(ctx context.Context, key interface{}) (bool, error) {
v, err := c.Get(ctx, key)
if err != nil {
return false, err
}
@ -259,7 +260,7 @@ 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) {
func (c *adapterMemory) Remove(ctx context.Context, keys ...interface{}) (value interface{}, err error) {
var removedKeys []interface{}
removedKeys, value, err = c.data.Remove(keys...)
if err != nil {
@ -275,33 +276,33 @@ func (c *adapterMemory) Remove(keys ...interface{}) (value interface{}, err erro
}
// Data returns a copy of all key-value pairs in the cache as map type.
func (c *adapterMemory) Data() (map[interface{}]interface{}, error) {
func (c *adapterMemory) Data(ctx context.Context) (map[interface{}]interface{}, error) {
return c.data.Data()
}
// Keys returns all keys in the cache as slice.
func (c *adapterMemory) Keys() ([]interface{}, error) {
func (c *adapterMemory) Keys(ctx context.Context) ([]interface{}, error) {
return c.data.Keys()
}
// Values returns all values in the cache as slice.
func (c *adapterMemory) Values() ([]interface{}, error) {
func (c *adapterMemory) Values(ctx context.Context) ([]interface{}, error) {
return c.data.Values()
}
// Size returns the size of the cache.
func (c *adapterMemory) Size() (size int, err error) {
func (c *adapterMemory) Size(ctx context.Context) (size int, err error) {
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 {
func (c *adapterMemory) Clear(ctx context.Context) error {
return c.data.Clear()
}
// Close closes the cache.
func (c *adapterMemory) Close() error {
func (c *adapterMemory) Close(ctx context.Context) error {
if c.cap > 0 {
c.lru.Close()
}

View File

@ -7,6 +7,7 @@
package gcache
import (
"context"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/os/gtimer"
"github.com/gogf/gf/util/gconv"
@ -15,7 +16,8 @@ import (
// Cache struct.
type Cache struct {
Adapter // Adapter for cache features.
adapter Adapter // Adapter for cache features.
ctx context.Context // Context for operations.
}
// New creates and returns a new cache object using default memory adapter.
@ -23,7 +25,7 @@ type Cache struct {
func New(lruCap ...int) *Cache {
memAdapter := newAdapterMemory(lruCap...)
c := &Cache{
Adapter: memAdapter,
adapter: memAdapter,
}
// Here may be a "timer leak" if adapter is manually changed from memory adapter.
// Do not worry about this, as adapter is less changed and it dose nothing if it's not used.
@ -31,11 +33,27 @@ func New(lruCap ...int) *Cache {
return c
}
// Clone returns a shallow copy of current object.
func (c *Cache) Clone() *Cache {
return &Cache{
adapter: c.adapter,
ctx: c.ctx,
}
}
// Ctx is a chaining function, which shallowly clones current object and sets the context
// for next operation.
func (c *Cache) Ctx(ctx context.Context) *Cache {
newCache := c.Clone()
newCache.ctx = ctx
return newCache
}
// SetAdapter changes the adapter for this cache.
// Be very note that, this setting function is not concurrent-safe, which means you should not call
// this setting function concurrently in multiple goroutines.
func (c *Cache) SetAdapter(adapter Adapter) {
c.Adapter = adapter
c.adapter = adapter
}
// GetVar retrieves and returns the value of <key> as gvar.Var.
@ -59,3 +77,11 @@ func (c *Cache) KeyStrings() ([]string, error) {
}
return gconv.Strings(keys), nil
}
// KeyStrings returns all keys in the cache as string slice.
func (c *Cache) getCtx() context.Context {
if c.ctx == nil {
return context.Background()
}
return c.ctx
}

View File

@ -0,0 +1,150 @@
// 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 (
"time"
)
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
//
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0.
func (c *Cache) Set(key interface{}, value interface{}, duration time.Duration) error {
return c.adapter.Set(c.getCtx(), 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 deletes the keys of <data> if <duration> < 0 or given <value> is nil.
func (c *Cache) Sets(data map[interface{}]interface{}, duration time.Duration) error {
return c.adapter.Sets(c.getCtx(), data, duration)
}
// SetIfNotExist sets cache with <key>-<value> pair which is expired after <duration>
// if <key> does not exist in the cache. It returns true the <key> dose not exist in the
// cache and it sets <value> successfully to the cache, or else it returns false.
//
// The parameter <value> can be type of <func() interface{}>, but it dose nothing if its
// result is nil.
//
// It does not expire if <duration> == 0.
// It deletes the <key> if <duration> < 0 or given <value> is nil.
func (c *Cache) SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) {
return c.adapter.SetIfNotExist(c.getCtx(), key, value, duration)
}
// Get retrieves and returns the associated value of given <key>.
// It returns nil if it does not exist, its value is nil or it's expired.
func (c *Cache) Get(key interface{}) (interface{}, error) {
return c.adapter.Get(c.getCtx(), key)
}
// GetOrSet retrieves and 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.
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
// if <value> is a function and the function result is nil.
func (c *Cache) GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) {
return c.adapter.GetOrSet(c.getCtx(), key, value, duration)
}
// GetOrSetFunc retrieves and 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.
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
// if <value> is a function and the function result is nil.
func (c *Cache) GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
return c.adapter.GetOrSetFunc(c.getCtx(), key, f, duration)
}
// GetOrSetFuncLock retrieves and 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.
// It does nothing if function <f> returns nil.
//
// Note that the function <f> should be executed within writing mutex lock for concurrent
// safety purpose.
func (c *Cache) GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
return c.adapter.GetOrSetFuncLock(c.getCtx(), key, f, duration)
}
// Contains returns true if <key> exists in the cache, or else returns false.
func (c *Cache) Contains(key interface{}) (bool, error) {
return c.adapter.Contains(c.getCtx(), key)
}
// GetExpire retrieves and returns the expiration of <key> in the cache.
//
// It returns 0 if the <key> does not expire.
// It returns -1 if the <key> does not exist in the cache.
func (c *Cache) GetExpire(key interface{}) (time.Duration, error) {
return c.adapter.GetExpire(c.getCtx(), key)
}
// Remove deletes one or more keys from cache, and returns its value.
// If multiple keys are given, it returns the value of the last deleted item.
func (c *Cache) Remove(keys ...interface{}) (value interface{}, err error) {
return c.adapter.Remove(c.getCtx(), keys...)
}
// Update updates the value of <key> without changing its expiration and returns the old value.
// The returned value <exist> is false if the <key> does not exist in the cache.
//
// It deletes the <key> if given <value> is nil.
// It does nothing if <key> does not exist in the cache.
func (c *Cache) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) {
return c.adapter.Update(c.getCtx(), key, value)
}
// UpdateExpire updates the expiration of <key> and returns the old expiration duration value.
//
// It returns -1 and does nothing if the <key> does not exist in the cache.
// It deletes the <key> if <duration> < 0.
func (c *Cache) UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) {
return c.adapter.UpdateExpire(c.getCtx(), key, duration)
}
// Size returns the number of items in the cache.
func (c *Cache) Size() (size int, err error) {
return c.adapter.Size(c.getCtx())
}
// Data returns a copy of all key-value pairs in the cache as map type.
// Note that this function may leads lots of memory usage, you can implement this function
// if necessary.
func (c *Cache) Data() (map[interface{}]interface{}, error) {
return c.adapter.Data(c.getCtx())
}
// Keys returns all keys in the cache as slice.
func (c *Cache) Keys() ([]interface{}, error) {
return c.adapter.Keys(c.getCtx())
}
// Values returns all values in the cache as slice.
func (c *Cache) Values() ([]interface{}, error) {
return c.adapter.Values(c.getCtx())
}
// Clear clears all data of the cache.
// Note that this function is sensitive and should be carefully used.
func (c *Cache) Clear() error {
return c.adapter.Clear(c.getCtx())
}
// Close closes the cache if necessary.
func (c *Cache) Close() error {
return c.adapter.Close(c.getCtx())
}

View File

@ -9,6 +9,7 @@
package gcache_test
import (
"context"
"github.com/gogf/gf/util/guid"
"math"
"testing"
@ -413,3 +414,14 @@ func TestCache_Basic(t *testing.T) {
}
})
}
func TestCache_Ctx(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
cache := gcache.New()
cache.Ctx(context.Background()).Sets(g.MapAnyAny{1: 11, 2: 22}, 0)
b, _ := cache.Contains(1)
t.Assert(b, true)
v, _ := cache.Get(1)
t.Assert(v, 11)
})
}