diff --git a/client/cache/random_expired_cache.go b/client/cache/random_expired_cache.go index eeebf880..26200112 100644 --- a/client/cache/random_expired_cache.go +++ b/client/cache/random_expired_cache.go @@ -17,12 +17,20 @@ package cache import ( "context" "math/rand" + "sync/atomic" "time" ) // RandomExpireCacheOption implement genreate random time offset expired option type RandomExpireCacheOption func(*RandomExpireCache) +// WithOffsetFunc returns a RandomExpireCacheOption that configures the offset function +func WithOffsetFunc(fn func() time.Duration) RandomExpireCacheOption { + return func(cache *RandomExpireCache) { + cache.offset = fn + } +} + // RandomExpireCache prevent cache batch invalidation // Cache random time offset expired type RandomExpireCache struct { @@ -38,15 +46,30 @@ func (rec *RandomExpireCache) Put(ctx context.Context, key string, val interface // NewRandomExpireCache return random expire cache struct func NewRandomExpireCache(adapter Cache, opts ...RandomExpireCacheOption) Cache { - var rec RandomExpireCache - rec.Cache = adapter + rec := RandomExpireCache{ + Cache: adapter, + offset: defaultExpiredFunc(), + } for _, fn := range opts { fn(&rec) } return &rec } -// defaultExpiredFunc genreate random time offset expired -func defaultExpiredFunc() time.Duration { - return time.Duration(rand.Intn(5)+3) * time.Second +// defaultExpiredFunc return a func that used to generate random time offset (range: [3s,8s)) expired +func defaultExpiredFunc() func() time.Duration { + const size = 5 + var randTimes [size]time.Duration + for i := range randTimes { + randTimes[i] = time.Duration(i+3) * time.Second + } + // shuffle values + for i := range randTimes { + n := rand.Intn(size) + randTimes[i], randTimes[n] = randTimes[n], randTimes[i] + } + var i uint64 + return func() time.Duration { + return randTimes[atomic.AddUint64(&i, 1)%size] + } } diff --git a/client/cache/random_expired_cache_test.go b/client/cache/random_expired_cache_test.go index b1af5840..1e3bb935 100644 --- a/client/cache/random_expired_cache_test.go +++ b/client/cache/random_expired_cache_test.go @@ -16,6 +16,7 @@ package cache import ( "context" + "math/rand" "strings" "testing" "time" @@ -27,10 +28,9 @@ func TestRandomExpireCache(t *testing.T) { bm, err := NewCache("memory", `{"interval":20}`) assert.Nil(t, err) - // cache := NewRandomExpireCache(bm) - cache := NewRandomExpireCache(bm, func(opt *RandomExpireCache) { - opt.offset = defaultExpiredFunc - }) + cache := NewRandomExpireCache(bm) + // should not be nil + assert.NotNil(t, cache.(*RandomExpireCache).offset) timeoutDuration := 3 * time.Second @@ -84,5 +84,16 @@ func TestRandomExpireCache(t *testing.T) { assert.NotNil(t, err) assert.True(t, strings.Contains(err.Error(), "key isn't exist")) - +} + +func TestWithOffsetFunc(t *testing.T) { + bm, err := NewCache("memory", `{"interval":20}`) + assert.Nil(t, err) + + magic := -time.Duration(rand.Int()) + cache := NewRandomExpireCache(bm, WithOffsetFunc(func() time.Duration { + return magic + })) + // offset should return the magic value + assert.Equal(t, magic, cache.(*RandomExpireCache).offset()) }