feature: add write though for cache mode (#5117)

* feature: add writethough for cache mode
This commit is contained in:
hookokoko 2022-12-17 10:25:39 +08:00 committed by Ming Deng
parent 882130421d
commit badab8eafd
4 changed files with 195 additions and 0 deletions

View File

@ -1,4 +1,5 @@
# developing
- [Fix 5117: support write though cache](https://github.com/beego/beego/pull/5117)
# v2.0.7
- [Upgrade github.com/go-kit/kit, CVE-2022-24450](https://github.com/beego/beego/pull/5121)

View File

@ -123,6 +123,16 @@ var InvalidSsdbCacheValue = berror.DefineCode(4002022, moduleName, "InvalidSsdbC
SSDB cache only accept string value. Please check your input.
`)
var InvalidInitParameters = berror.DefineCode(4002025, moduleName, "InvalidInitParameters", `
Invalid init cache parameters.
You can check the related function to confirm that if you pass correct parameters or configure to initiate a Cache instance.
`)
var PersistCacheFailed = berror.DefineCode(4002026, moduleName, "PersistCacheFailed", `
Failed to execute the StoreFunc.
Please check the log to make sure the StoreFunc works for the specific key and value.
`)
var InvalidLoadFunc = berror.DefineCode(4002023, moduleName, "InvalidLoadFunc", `
Invalid load function for read-through pattern decorator.
You should pass a valid(non-nil) load function when initiate the decorator instance.

48
client/cache/write_through.go vendored Normal file
View File

@ -0,0 +1,48 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cache
import (
"context"
"fmt"
"time"
"github.com/beego/beego/v2/core/berror"
)
type WriteThoughCache 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) {
if fn == nil || cache == nil {
return nil, berror.Error(InvalidInitParameters, "cache or storeFunc can not be nil")
}
w := &WriteThoughCache{
Cache: cache,
storeFunc: fn,
}
return w, nil
}
func (w *WriteThoughCache) 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))
}
return w.Cache.Put(ctx, key, val, expiration)
}

136
client/cache/write_through_test.go vendored Normal file
View File

@ -0,0 +1,136 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// nolint
package cache
import (
"context"
"errors"
"fmt"
"testing"
"time"
"github.com/beego/beego/v2/core/berror"
"github.com/stretchr/testify/assert"
)
func TestWriteThoughCache_Set(t *testing.T) {
var mockDbStore = make(map[string]any)
testCases := []struct {
name string
cache Cache
storeFunc func(ctx context.Context, key string, val any) error
key string
value any
wantErr error
}{
{
name: "store key/value in db fail",
cache: NewMemoryCache(),
storeFunc: func(ctx context.Context, key string, val any) error {
return errors.New("failed")
},
wantErr: berror.Wrap(errors.New("failed"), PersistCacheFailed,
fmt.Sprintf("key: %s, val: %v", "", nil)),
},
{
name: "store key/value success",
cache: NewMemoryCache(),
storeFunc: func(ctx context.Context, key string, val any) error {
mockDbStore[key] = val
return nil
},
key: "hello",
value: "world",
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
w, err := NewWriteThoughCache(tt.cache, tt.storeFunc)
if err != nil {
assert.EqualError(t, tt.wantErr, err.Error())
return
}
err = w.Set(context.Background(), tt.key, tt.value, 60*time.Second)
if err != nil {
assert.EqualError(t, tt.wantErr, err.Error())
return
}
val, err := w.Get(context.Background(), tt.key)
assert.Nil(t, err)
assert.Equal(t, tt.value, val)
vv, ok := mockDbStore[tt.key]
assert.True(t, ok)
assert.Equal(t, tt.value, vv)
})
}
}
func TestNewWriteThoughCache(t *testing.T) {
underlyingCache := NewMemoryCache()
storeFunc := func(ctx context.Context, key string, val any) error { return nil }
type args struct {
cache Cache
fn func(ctx context.Context, key string, val any) error
}
tests := []struct {
name string
args args
wantRes *WriteThoughCache
wantErr error
}{
{
name: "nil cache parameters",
args: args{
cache: nil,
fn: storeFunc,
},
wantErr: berror.Error(InvalidInitParameters, "cache or storeFunc can not be nil"),
},
{
name: "nil storeFunc parameters",
args: args{
cache: underlyingCache,
fn: nil,
},
wantErr: berror.Error(InvalidInitParameters, "cache or storeFunc can not be nil"),
},
{
name: "init write-though cache success",
args: args{
cache: underlyingCache,
fn: storeFunc,
},
wantRes: &WriteThoughCache{
Cache: underlyingCache,
storeFunc: storeFunc,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := NewWriteThoughCache(tt.args.cache, tt.args.fn)
assert.Equal(t, tt.wantErr, err)
if err != nil {
return
}
})
}
}