feature: add write though for cache mode (#5117)
* feature: add writethough for cache mode
This commit is contained in:
parent
882130421d
commit
badab8eafd
@ -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)
|
||||
|
||||
10
client/cache/error_code.go
vendored
10
client/cache/error_code.go
vendored
@ -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
48
client/cache/write_through.go
vendored
Normal 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
136
client/cache/write_through_test.go
vendored
Normal 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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user