Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add cache options #44

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 40 additions & 14 deletions cache/memory/base_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Han-Ya-Jun marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down Expand Up @@ -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
}

Expand Down
6 changes: 3 additions & 3 deletions cache/memory/base_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 6 additions & 4 deletions cache/memory/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
30 changes: 28 additions & 2 deletions cache/memory/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ package memory

import (
"context"
"errors"
"time"

. "github.com/onsi/ginkgo/v2"
Expand All @@ -21,20 +22,45 @@ 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)
})

It("NewMock", 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)

})

})
Loading