Skip to content

Commit

Permalink
cache: fix typo and optimize the naming
Browse files Browse the repository at this point in the history
  • Loading branch information
flycash committed May 26, 2023
1 parent d8ffaff commit 84abb70
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 45 deletions.
14 changes: 7 additions & 7 deletions client/cache/bloom_filter_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (

type BloomFilterCache struct {
Cache
BloomFilter
blm BloomFilter
loadFunc func(ctx context.Context, key string) (any, error)
expiration time.Duration // set cache expiration, default never expire
}
Expand All @@ -34,18 +34,18 @@ type BloomFilter interface {
Add(data string)
}

func NewBloomFilterCache(cache Cache, ln func(context.Context, string) (any, error), blm BloomFilter,
func NewBloomFilterCache(cache Cache, ln func(ctx context.Context, key string) (any, error), blm BloomFilter,
expiration time.Duration,
) (*BloomFilterCache, error) {
if cache == nil || ln == nil || blm == nil {
return nil, berror.Error(InvalidInitParameters, "missing required parameters")
}

return &BloomFilterCache{
Cache: cache,
BloomFilter: blm,
loadFunc: ln,
expiration: expiration,
Cache: cache,
blm: blm,
loadFunc: ln,
expiration: expiration,
}, nil
}

Expand All @@ -55,7 +55,7 @@ func (bfc *BloomFilterCache) Get(ctx context.Context, key string) (any, error) {
return nil, err
}
if errors.Is(err, ErrKeyNotExist) {
exist := bfc.BloomFilter.Test(key)
exist := bfc.blm.Test(key)
if exist {
val, err = bfc.loadFunc(ctx, key)
if err != nil {
Expand Down
55 changes: 29 additions & 26 deletions client/cache/bloom_filter_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cache
import (
"context"
"errors"
"fmt"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -175,29 +176,31 @@ func TestBloomFilterCache_Get(t *testing.T) {
}
}

// This implementation of Bloom filters cache is NOT safe for concurrent use.
// Uncomment the following method.
// func TestBloomFilterCache_Get_Concurrency(t *testing.T) {
// bfc, err := NewBloomFilterCache(cacheUnderlying, loadFunc, mockBloom, time.Minute)
// assert.Nil(t, err)
//
// _ = mockDB.Db.ClearAll(context.Background())
// _ = mockDB.Db.Put(context.Background(), "key_11", "value_11", 0)
// mockBloom.AddString("key_11")
//
// var wg sync.WaitGroup
// wg.Add(100000)
// for i := 0; i < 100000; i++ {
// key := fmt.Sprintf("key_%d", i)
// go func(key string) {
// defer wg.Done()
// val, _ := bfc.Get(context.Background(), key)
//
// if val != nil {
// assert.Equal(t, "value_11", val)
// }
// }(key)
// }
// wg.Wait()
// assert.Equal(t, int64(1), mockDB.loadCnt)
// }
func ExampleNewBloomFilterCache() {
c := NewMemoryCache()
c, err := NewBloomFilterCache(c, func(ctx context.Context, key string) (any, error) {
return fmt.Sprintf("hello, %s", key), nil
}, &AlwaysExist{}, time.Minute)
if err != nil {
panic(err)
}

val, err := c.Get(context.Background(), "Beego")
if err != nil {
panic(err)
}
fmt.Println(val)
// Output:
// hello, Beego
}

type AlwaysExist struct {
}

func (a *AlwaysExist) Test(data string) bool {
return true
}

func (a *AlwaysExist) Add(data string) {

}
4 changes: 2 additions & 2 deletions client/cache/random_expired_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import (
// 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 {
// WithRandomExpireOffsetFunc returns a RandomExpireCacheOption that configures the offset function
func WithRandomExpireOffsetFunc(fn func() time.Duration) RandomExpireCacheOption {
return func(cache *RandomExpireCache) {
cache.offset = fn
}
Expand Down
33 changes: 31 additions & 2 deletions client/cache/random_expired_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package cache

import (
"context"
"fmt"
"math/rand"
"strings"
"testing"
Expand Down Expand Up @@ -86,14 +87,42 @@ func TestRandomExpireCache(t *testing.T) {
assert.True(t, strings.Contains(err.Error(), "key isn't exist"))
}

func TestWithOffsetFunc(t *testing.T) {
func TestWithRandomExpireOffsetFunc(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 {
cache := NewRandomExpireCache(bm, WithRandomExpireOffsetFunc(func() time.Duration {
return magic
}))
// offset should return the magic value
assert.Equal(t, magic, cache.(*RandomExpireCache).offset())
}

func ExampleNewRandomExpireCache() {
mc := NewMemoryCache()
// use the default strategy which will generate random time offset (range: [3s,8s)) expired
c := NewRandomExpireCache(mc)
// so the expiration will be [1m3s, 1m8s)
err := c.Put(context.Background(), "hello", "world", time.Minute)
if err != nil {
panic(err)
}

c = NewRandomExpireCache(mc,
// based on the expiration
WithRandomExpireOffsetFunc(func() time.Duration {
val := rand.Int31n(100)
fmt.Printf("calculate offset")
return time.Duration(val) * time.Second
}))

// so the expiration will be [1m0s, 1m100s)
err = c.Put(context.Background(), "hello", "world", time.Minute)
if err != nil {
panic(err)
}

// Output:
// calculate offset
}
26 changes: 26 additions & 0 deletions client/cache/read_through_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package cache
import (
"context"
"errors"
"fmt"
"testing"
"time"

Expand Down Expand Up @@ -143,3 +144,28 @@ func (m *MockOrm) Load(key string) (any, error) {
}
return m.kvs[key], nil
}

func ExampleNewReadThroughCache() {
c := NewMemoryCache()
var err error
c, err = NewReadThroughCache(c,
// expiration, same as the expiration of key
time.Minute,
// load func, how to load data if the key is absent.
// in general, you should load data from database.
func(ctx context.Context, key string) (any, error) {
return fmt.Sprintf("hello, %s", key), nil
})
if err != nil {
panic(err)
}

val, err := c.Get(context.Background(), "Beego")
if err != nil {
panic(err)
}
fmt.Print(val)

// Output:
// hello, Beego
}
18 changes: 18 additions & 0 deletions client/cache/singleflight_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package cache

import (
"context"
"fmt"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -70,3 +71,20 @@ func testSingleflightCacheConcurrencyGet(t *testing.T, bm Cache) {
}
wg.Wait()
}

func ExampleNewSingleflightCache() {
c := NewMemoryCache()
c, err := NewSingleflightCache(c, time.Minute, func(ctx context.Context, key string) (any, error) {
return fmt.Sprintf("hello, %s", key), nil
})
if err != nil {
panic(err)
}
val, err := c.Get(context.Background(), "Beego")
if err != nil {
panic(err)
}
fmt.Print(val)
// Output:
// hello, Beego
}
10 changes: 6 additions & 4 deletions client/cache/write_through.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,26 @@ import (
"github.com/beego/beego/v2/core/berror"
)

type WriteThoughCache struct {
type WriteThroughCache struct {
Cache
storeFunc func(ctx context.Context, key string, val any) error
}

func NewWriteThoughCache(cache Cache, fn func(ctx context.Context, key string, val any) error) (*WriteThoughCache, error) {
// NewWriteThroughCache creates a write through cache pattern decorator.
// The fn is the function that persistent the key and val.
func NewWriteThroughCache(cache Cache, fn func(ctx context.Context, key string, val any) error) (*WriteThroughCache, error) {
if fn == nil || cache == nil {
return nil, berror.Error(InvalidInitParameters, "cache or storeFunc can not be nil")
}

w := &WriteThoughCache{
w := &WriteThroughCache{
Cache: cache,
storeFunc: fn,
}
return w, nil
}

func (w *WriteThoughCache) Set(ctx context.Context, key string, val any, expiration time.Duration) error {
func (w *WriteThroughCache) Set(ctx context.Context, key string, val any, expiration time.Duration) error {
err := w.storeFunc(ctx, key, val)
if err != nil {
return berror.Wrap(err, PersistCacheFailed, fmt.Sprintf("key: %s, val: %v", key, val))
Expand Down
26 changes: 22 additions & 4 deletions client/cache/write_through_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func TestWriteThoughCache_Set(t *testing.T) {
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
w, err := NewWriteThoughCache(tt.cache, tt.storeFunc)
w, err := NewWriteThroughCache(tt.cache, tt.storeFunc)
if err != nil {
assert.EqualError(t, tt.wantErr, err.Error())
return
Expand Down Expand Up @@ -94,7 +94,7 @@ func TestNewWriteThoughCache(t *testing.T) {
tests := []struct {
name string
args args
wantRes *WriteThoughCache
wantRes *WriteThroughCache
wantErr error
}{
{
Expand All @@ -119,19 +119,37 @@ func TestNewWriteThoughCache(t *testing.T) {
cache: underlyingCache,
fn: storeFunc,
},
wantRes: &WriteThoughCache{
wantRes: &WriteThroughCache{
Cache: underlyingCache,
storeFunc: storeFunc,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := NewWriteThoughCache(tt.args.cache, tt.args.fn)
_, err := NewWriteThroughCache(tt.args.cache, tt.args.fn)
assert.Equal(t, tt.wantErr, err)
if err != nil {
return
}
})
}
}

func ExampleNewWriteThroughCache() {
c := NewMemoryCache()
wtc, err := NewWriteThroughCache(c, func(ctx context.Context, key string, val any) error {
fmt.Printf("write data to somewhere key %s, val %v \n", key, val)
return nil
})
if err != nil {
panic(err)
}
err = wtc.Set(context.Background(),
"/biz/user/id=1", "I am user 1", time.Minute)
if err != nil {
panic(err)
}
// Output:
// write data to somewhere key /biz/user/id=1, val I am user 1
}

0 comments on commit 84abb70

Please sign in to comment.