From b73efb537acfb9ee4f0cd1bedd60124138fce158 Mon Sep 17 00:00:00 2001 From: hanyajun <1581532052@qq.com> Date: Tue, 30 Jul 2024 15:16:48 +0800 Subject: [PATCH 1/4] feat: add cache options --- cache/memory/base_cache.go | 52 ++++++++++++++++++++++++--------- cache/memory/base_cache_test.go | 6 ++-- cache/memory/cache.go | 10 ++++--- cache/memory/cache_test.go | 2 +- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/cache/memory/base_cache.go b/cache/memory/base_cache.go index e424c1a..da397e1 100644 --- a/cache/memory/base_cache.go +++ b/cache/memory/base_cache.go @@ -24,22 +24,46 @@ import ( const EmptyCacheExpiration = 5 * time.Second -// NewBaseCache creates a new baseCache instance which implements Cache interface. -func NewBaseCache(disabled bool, retrieveFunc RetrieveFunc, backend backend.Backend) Cache { - return &BaseCache{ - backend: backend, - disabled: disabled, - retrieveFunc: retrieveFunc, - } -} - // BaseCache is a cache which retrieves data from the backend and stores it in the cache. type BaseCache struct { backend backend.Backend - disabled bool - retrieveFunc RetrieveFunc - g singleflight.Group + disabled bool + retrieveFunc RetrieveFunc + g singleflight.Group + withEmptyCache bool + emptyCacheExpireDuration time.Duration +} + +type Option func(*BaseCache) + +func WithNoCache() Option { + return func(cache *BaseCache) { + cache.disabled = true + } +} + +// WithEmptyCache will set the key EmptyCache if retrieve fail from retrieveFunc +func WithEmptyCache(timeout time.Duration) Option { + return func(cache *BaseCache) { + if timeout == 0 { + timeout = EmptyCacheExpiration + } + cache.withEmptyCache = true + cache.emptyCacheExpireDuration = timeout + } +} + +// NewBaseCache creates a new baseCache instance which implements Cache interface. +func NewBaseCache(retrieveFunc RetrieveFunc, backend backend.Backend, options ...Option) Cache { + c := &BaseCache{ + backend: backend, + retrieveFunc: retrieveFunc, + } + for _, o := range options { + o(c) + } + return c } // EmptyCache is a placeholder for the missing key @@ -90,9 +114,9 @@ func (c *BaseCache) doRetrieve(ctx context.Context, k cache.Key) (interface{}, e return c.retrieveFunc(ctx, k) }) - if err != nil { + if err != nil && c.withEmptyCache { // ! if error, cache it too, make it short enough(5s) - c.backend.Set(key, EmptyCache{err: err}, EmptyCacheExpiration) + c.backend.Set(key, EmptyCache{err: err}, c.emptyCacheExpireDuration) return nil, err } diff --git a/cache/memory/base_cache_test.go b/cache/memory/base_cache_test.go index 840ed73..0113c71 100644 --- a/cache/memory/base_cache_test.go +++ b/cache/memory/base_cache_test.go @@ -77,7 +77,7 @@ var _ = Describe("BaseCache", func() { expiration := 5 * time.Minute be = backend.NewMemoryBackend("test", expiration, nil) - c = NewBaseCache(false, retrieveTest, be) + c = NewBaseCache(retrieveTest, be, WithEmptyCache(0)) ctx = context.Background() }) @@ -105,7 +105,7 @@ var _ = Describe("BaseCache", func() { }) It("Disabled then get", func() { - c = NewBaseCache(true, retrieveTest, be) + c = NewBaseCache(retrieveTest, be, WithNoCache()) aKey := cache.NewStringKey("a") x, err := c.Get(ctx, aKey) @@ -378,7 +378,7 @@ var _ = Describe("BaseCache", func() { }) It("retrieveError", func() { - c = NewBaseCache(true, retrieveError, be) + c = NewBaseCache(retrieveError, be, WithNoCache()) assert.NotNil(GinkgoT(), c) aKey := cache.NewStringKey("a") _, err := c.Get(ctx, aKey) diff --git a/cache/memory/cache.go b/cache/memory/cache.go index 8cbe3d2..2d63b47 100644 --- a/cache/memory/cache.go +++ b/cache/memory/cache.go @@ -19,24 +19,26 @@ import ( // NewCache creates a new cache object. // - name: the cache name. -// - disabled: whether the cache is disabled. // - retrieveFunc: the function to retrieve the real data. // - expiration: the expiration time. // - randomExtraExpirationFunc: the function to generate a random duration, used to add extra expiration for each key. +// - options: the options for the cache . eg: +// WithNoCache(disable cache) +// WithEmptyCache(set the key EmptyCache if retrieve fail from retrieveFunc) func NewCache( name string, - disabled bool, retrieveFunc RetrieveFunc, expiration time.Duration, randomExtraExpirationFunc backend.RandomExtraExpirationDurationFunc, + options ...Option, ) Cache { be := backend.NewMemoryBackend(name, expiration, randomExtraExpirationFunc) - return NewBaseCache(disabled, retrieveFunc, be) + return NewBaseCache(retrieveFunc, be, options...) } // NewMockCache create a memory cache for mock func NewMockCache(retrieveFunc RetrieveFunc) Cache { be := backend.NewMemoryBackend("mockCache", 5*time.Minute, nil) - return NewBaseCache(false, retrieveFunc, be) + return NewBaseCache(retrieveFunc, be) } diff --git a/cache/memory/cache_test.go b/cache/memory/cache_test.go index 00e35e8..9652310 100644 --- a/cache/memory/cache_test.go +++ b/cache/memory/cache_test.go @@ -29,7 +29,7 @@ var _ = Describe("Cache", func() { It("New", func() { expiration := 5 * time.Minute - c := NewCache("test", false, retrieveOK, expiration, nil) + c := NewCache("test", retrieveOK, expiration, nil) assert.NotNil(GinkgoT(), c) }) From 9608ba098e60dade6559abe9f74baf1f59309da7 Mon Sep 17 00:00:00 2001 From: hanyajun <1581532052@qq.com> Date: Tue, 30 Jul 2024 15:57:45 +0800 Subject: [PATCH 2/4] feat: add cache options --- cache/memory/base_cache.go | 8 +++++--- cache/memory/cache.go | 4 ++-- cache/memory/cache_test.go | 24 ++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/cache/memory/base_cache.go b/cache/memory/base_cache.go index da397e1..5d91df7 100644 --- a/cache/memory/base_cache.go +++ b/cache/memory/base_cache.go @@ -114,9 +114,11 @@ func (c *BaseCache) doRetrieve(ctx context.Context, k cache.Key) (interface{}, e return c.retrieveFunc(ctx, k) }) - if err != nil && c.withEmptyCache { - // ! if error, cache it too, make it short enough(5s) - c.backend.Set(key, EmptyCache{err: err}, c.emptyCacheExpireDuration) + if err != nil { + if c.withEmptyCache { + // ! if error, cache it too, make it short enough(5s) + c.backend.Set(key, EmptyCache{err: err}, c.emptyCacheExpireDuration) + } return nil, err } diff --git a/cache/memory/cache.go b/cache/memory/cache.go index 2d63b47..89a8250 100644 --- a/cache/memory/cache.go +++ b/cache/memory/cache.go @@ -23,8 +23,8 @@ import ( // - expiration: the expiration time. // - randomExtraExpirationFunc: the function to generate a random duration, used to add extra expiration for each key. // - options: the options for the cache . eg: -// WithNoCache(disable cache) -// WithEmptyCache(set the key EmptyCache if retrieve fail from retrieveFunc) +// WithNoCache:disable cache +// WithEmptyCache(duration): set the key EmptyCache if retrieve fail from retrieveFunc func NewCache( name string, retrieveFunc RetrieveFunc, diff --git a/cache/memory/cache_test.go b/cache/memory/cache_test.go index 9652310..9e86ce1 100644 --- a/cache/memory/cache_test.go +++ b/cache/memory/cache_test.go @@ -13,6 +13,7 @@ package memory import ( "context" + "errors" "time" . "github.com/onsi/ginkgo/v2" @@ -25,7 +26,14 @@ func retrieveOK(ctx context.Context, k cache.Key) (interface{}, error) { return "ok", nil } +func retrieveErr(ctx context.Context, k cache.Key) (interface{}, error) { + return "ok", errors.ErrUnsupported +} + var _ = Describe("Cache", func() { + + var ctx context.Context + It("New", func() { expiration := 5 * time.Minute @@ -37,4 +45,20 @@ var _ = Describe("Cache", func() { c := NewMockCache(retrieveOK) assert.NotNil(GinkgoT(), c) }) + + It("Cache Disable", func() { + expiration := 5 * time.Minute + c := NewCache("test", retrieveOK, expiration, nil, WithNoCache()) + assert.True(GinkgoT(), c.Disabled()) + }) + + It("Cache WithEmptyCache", func() { + aKey := cache.NewStringKey("test") + expiration := 5 * time.Minute + c := NewCache("test", retrieveErr, expiration, nil, WithEmptyCache(0)) + _, err := c.Get(ctx, aKey) + assert.ErrorIs(GinkgoT(), err, errors.ErrUnsupported) + + }) + }) From 3929eac73091900235151c607ac956918440a3e6 Mon Sep 17 00:00:00 2001 From: hanyajun <1581532052@qq.com> Date: Tue, 30 Jul 2024 15:58:53 +0800 Subject: [PATCH 3/4] feat: add cache options --- cache/memory/cache_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cache/memory/cache_test.go b/cache/memory/cache_test.go index 9e86ce1..02af7e9 100644 --- a/cache/memory/cache_test.go +++ b/cache/memory/cache_test.go @@ -23,11 +23,11 @@ import ( ) func retrieveOK(ctx context.Context, k cache.Key) (interface{}, error) { - return "ok", nil + return "", nil } func retrieveErr(ctx context.Context, k cache.Key) (interface{}, error) { - return "ok", errors.ErrUnsupported + return nil, errors.ErrUnsupported } var _ = Describe("Cache", func() { From aeaee1f138a79fdcef7b9a8d280f27efdf5e9a3f Mon Sep 17 00:00:00 2001 From: hanyajun <1581532052@qq.com> Date: Tue, 30 Jul 2024 16:05:01 +0800 Subject: [PATCH 4/4] fix: fix test --- cache/memory/cache_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cache/memory/cache_test.go b/cache/memory/cache_test.go index 02af7e9..55da1e6 100644 --- a/cache/memory/cache_test.go +++ b/cache/memory/cache_test.go @@ -22,12 +22,14 @@ import ( "github.com/TencentBlueKing/gopkg/cache" ) +var cacheError = errors.New("cache error") + func retrieveOK(ctx context.Context, k cache.Key) (interface{}, error) { return "", nil } func retrieveErr(ctx context.Context, k cache.Key) (interface{}, error) { - return nil, errors.ErrUnsupported + return nil, cacheError } var _ = Describe("Cache", func() { @@ -57,7 +59,7 @@ var _ = Describe("Cache", func() { expiration := 5 * time.Minute c := NewCache("test", retrieveErr, expiration, nil, WithEmptyCache(0)) _, err := c.Get(ctx, aKey) - assert.ErrorIs(GinkgoT(), err, errors.ErrUnsupported) + assert.ErrorIs(GinkgoT(), err, cacheError) })