From c01b520ba64f2e3749b57fd0646a9c154483c760 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 26 Jan 2021 14:11:36 +0800 Subject: [PATCH] improve package gcache --- os/gcache/gcache.go | 7 + os/gcache/gcache_adapter.go | 37 ++--- os/gcache/gcache_adapter_memory.go | 47 +++--- os/gcache/gcache_cache.go | 32 +++- os/gcache/gcache_cache_adapter.go | 150 ++++++++++++++++++ ..._1_test.go => gcache_z_unit_basic_test.go} | 12 ++ 6 files changed, 241 insertions(+), 44 deletions(-) create mode 100644 os/gcache/gcache_cache_adapter.go rename os/gcache/{gcache_z_unit_1_test.go => gcache_z_unit_basic_test.go} (97%) diff --git a/os/gcache/gcache.go b/os/gcache/gcache.go index fb2fd09c9..6bfb36d48 100644 --- a/os/gcache/gcache.go +++ b/os/gcache/gcache.go @@ -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 - pair, which is expired after . // It does not expire if == 0. func Set(key interface{}, value interface{}, duration time.Duration) { diff --git a/os/gcache/gcache_adapter.go b/os/gcache/gcache_adapter.go index 03b9f63bc..f12c32570 100644 --- a/os/gcache/gcache_adapter.go +++ b/os/gcache/gcache_adapter.go @@ -7,6 +7,7 @@ package gcache import ( + "context" "time" ) @@ -16,13 +17,13 @@ type Adapter interface { // // It does not expire if == 0. // It deletes the if < 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 , which is expired after . // // It does not expire if == 0. // It deletes the keys of if < 0 or given 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 - pair which is expired after // if does not exist in the cache. It returns true the dose not exist in the @@ -33,11 +34,11 @@ type Adapter interface { // // It does not expire if == 0. // It deletes the if < 0 or given 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 . // 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 , or sets - pair and // returns if does not exist in the cache. The key-value pair expires @@ -46,7 +47,7 @@ type Adapter interface { // It does not expire if == 0. // It deletes the if < 0 or given is nil, but it does nothing // if 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 , or sets with result of // function and returns its result if does not exist in the cache. The key-value @@ -55,7 +56,7 @@ type Adapter interface { // It does not expire if == 0. // It deletes the if < 0 or given is nil, but it does nothing // if 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 , or sets with result of // function and returns its result if does not exist in the cache. The key-value @@ -66,52 +67,52 @@ type Adapter interface { // // Note that the function 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 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 in the cache. // // It returns 0 if the does not expire. // It returns -1 if the 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 without changing its expiration and returns the old value. // The returned value is false if the does not exist in the cache. // // It deletes the if given is nil. // It does nothing if 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 and returns the old expiration duration value. // // It returns -1 and does nothing if the does not exist in the cache. // It deletes the if < 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 } diff --git a/os/gcache/gcache_adapter_memory.go b/os/gcache/gcache_adapter_memory.go index 5bad1bb3f..cc8dba5b2 100644 --- a/os/gcache/gcache_adapter_memory.go +++ b/os/gcache/gcache_adapter_memory.go @@ -7,6 +7,7 @@ package gcache import ( + "context" "math" "time" @@ -72,7 +73,7 @@ func newAdapterMemory(lruCap ...int) *adapterMemory { // // It does not expire if == 0. // It deletes the if < 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 if given is nil. // It does nothing if 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 does not exist in the cache. // It deletes the if < 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 does not expire. // It returns -1 if the 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 == 0. // It deletes the if < 0 or given 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 == 0. // It deletes the keys of if < 0 or given 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 . // 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 == 0. // It deletes the if < 0 or given is nil, but it does nothing // if 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 == 0. // It deletes the if < 0 or given is nil, but it does nothing // if 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 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 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() } diff --git a/os/gcache/gcache_cache.go b/os/gcache/gcache_cache.go index c664c141a..54f5ea626 100644 --- a/os/gcache/gcache_cache.go +++ b/os/gcache/gcache_cache.go @@ -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 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 +} diff --git a/os/gcache/gcache_cache_adapter.go b/os/gcache/gcache_cache_adapter.go new file mode 100644 index 000000000..0f4f50852 --- /dev/null +++ b/os/gcache/gcache_cache_adapter.go @@ -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 - pair, which is expired after . +// +// It does not expire if == 0. +// It deletes the if < 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 , which is expired after . +// +// It does not expire if == 0. +// It deletes the keys of if < 0 or given 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 - pair which is expired after +// if does not exist in the cache. It returns true the dose not exist in the +// cache and it sets successfully to the cache, or else it returns false. +// +// The parameter can be type of , but it dose nothing if its +// result is nil. +// +// It does not expire if == 0. +// It deletes the if < 0 or given 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 . +// 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 , or sets - pair and +// returns if does not exist in the cache. The key-value pair expires +// after . +// +// It does not expire if == 0. +// It deletes the if < 0 or given is nil, but it does nothing +// if 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 , or sets with result of +// function and returns its result if does not exist in the cache. The key-value +// pair expires after . +// +// It does not expire if == 0. +// It deletes the if < 0 or given is nil, but it does nothing +// if 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 , or sets with result of +// function and returns its result if does not exist in the cache. The key-value +// pair expires after . +// +// It does not expire if == 0. +// It does nothing if function returns nil. +// +// Note that the function 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 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 in the cache. +// +// It returns 0 if the does not expire. +// It returns -1 if the 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 without changing its expiration and returns the old value. +// The returned value is false if the does not exist in the cache. +// +// It deletes the if given is nil. +// It does nothing if 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 and returns the old expiration duration value. +// +// It returns -1 and does nothing if the does not exist in the cache. +// It deletes the if < 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()) +} diff --git a/os/gcache/gcache_z_unit_1_test.go b/os/gcache/gcache_z_unit_basic_test.go similarity index 97% rename from os/gcache/gcache_z_unit_1_test.go rename to os/gcache/gcache_z_unit_basic_test.go index bf5a0ad81..9ea748cd3 100644 --- a/os/gcache/gcache_z_unit_1_test.go +++ b/os/gcache/gcache_z_unit_basic_test.go @@ -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) + }) +}