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

cache: fix typo and optimize the naming #5223

Merged
merged 1 commit into from
May 27, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [Fix 4435: Controller SaveToFile remove all temp file](https://github.com/beego/beego/pull/5138)
- [Fix 5079: Split signalChan into flushChan and closeChan](https://github.com/beego/beego/pull/5139)
- [Fix 5172: protect field access with lock to avoid possible data race](https://github.com/beego/beego/pull/5210)
- [cache: fix typo and optimize the naming]()

# v2.0.7
- [Upgrade github.com/go-kit/kit, CVE-2022-24450](https://github.com/beego/beego/pull/5121)
Expand Down
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 (*AlwaysExist) Test(string) bool {
return true
}

func (*AlwaysExist) Add(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
}