diff --git a/cache/memory/base_cache.go b/cache/memory/base_cache.go index e424c1a..5d91df7 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 @@ -91,8 +115,10 @@ func (c *BaseCache) doRetrieve(ctx context.Context, k cache.Key) (interface{}, e }) if err != nil { - // ! if error, cache it too, make it short enough(5s) - c.backend.Set(key, EmptyCache{err: err}, EmptyCacheExpiration) + 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/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..89a8250 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(duration): 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..55da1e6 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" @@ -21,15 +22,24 @@ import ( "github.com/TencentBlueKing/gopkg/cache" ) +var cacheError = errors.New("cache error") + 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 nil, cacheError } var _ = Describe("Cache", func() { + + var ctx context.Context + It("New", func() { expiration := 5 * time.Minute - c := NewCache("test", false, retrieveOK, expiration, nil) + c := NewCache("test", retrieveOK, expiration, nil) assert.NotNil(GinkgoT(), c) }) @@ -37,4 +47,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, cacheError) + + }) + })