Merge pull request #4493 from flycash/session-cache
error code for cache module
This commit is contained in:
commit
1ae542255c
@ -1,4 +1,5 @@
|
||||
# developing
|
||||
- Error codes definition of cache module. [4493](https://github.com/beego/beego/pull/4493)
|
||||
- Remove generateCommentRoute http hook. Using `bee generate routers` commands instead.[4486](https://github.com/beego/beego/pull/4486) [bee PR 762](https://github.com/beego/bee/pull/762)
|
||||
- Fix: /abc.html/aaa match /abc/aaa. [4459](https://github.com/beego/beego/pull/4459)
|
||||
- ORM mock. [4407](https://github.com/beego/beego/pull/4407)
|
||||
|
||||
@ -1 +1,5 @@
|
||||
# Error Module
|
||||
|
||||
## Module code
|
||||
- httplib 1
|
||||
- cache 2
|
||||
9
client/cache/cache.go
vendored
9
client/cache/cache.go
vendored
@ -33,8 +33,9 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
// Cache interface contains all behaviors for cache adapter.
|
||||
@ -55,12 +56,14 @@ type Cache interface {
|
||||
// Set a cached value with key and expire time.
|
||||
Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error
|
||||
// Delete cached value by key.
|
||||
// Should not return error if key not found
|
||||
Delete(ctx context.Context, key string) error
|
||||
// Increment a cached int value by key, as a counter.
|
||||
Incr(ctx context.Context, key string) error
|
||||
// Decrement a cached int value by key, as a counter.
|
||||
Decr(ctx context.Context, key string) error
|
||||
// Check if a cached value exists or not.
|
||||
// if key is expired, return (false, nil)
|
||||
IsExist(ctx context.Context, key string) (bool, error)
|
||||
// Clear all cache.
|
||||
ClearAll(ctx context.Context) error
|
||||
@ -78,7 +81,7 @@ var adapters = make(map[string]Instance)
|
||||
// it panics.
|
||||
func Register(name string, adapter Instance) {
|
||||
if adapter == nil {
|
||||
panic("cache: Register adapter is nil")
|
||||
panic(berror.Error(NilCacheAdapter, "cache: Register adapter is nil").Error())
|
||||
}
|
||||
if _, ok := adapters[name]; ok {
|
||||
panic("cache: Register called twice for adapter " + name)
|
||||
@ -92,7 +95,7 @@ func Register(name string, adapter Instance) {
|
||||
func NewCache(adapterName, config string) (adapter Cache, err error) {
|
||||
instanceFunc, ok := adapters[adapterName]
|
||||
if !ok {
|
||||
err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
|
||||
err = berror.Errorf(UnknownAdapter, "cache: unknown adapter name %s (forgot to import?)", adapterName)
|
||||
return
|
||||
}
|
||||
adapter = instanceFunc()
|
||||
|
||||
3
client/cache/cache_test.go
vendored
3
client/cache/cache_test.go
vendored
@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@ -108,7 +109,7 @@ func TestCache(t *testing.T) {
|
||||
assert.Equal(t, "author1", vv[1])
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "key [astaxie0] error: key not exist", err.Error())
|
||||
assert.True(t, strings.Contains(err.Error(), "key isn't exist"))
|
||||
}
|
||||
|
||||
func TestFileCache(t *testing.T) {
|
||||
|
||||
39
client/cache/calc_utils.go
vendored
39
client/cache/calc_utils.go
vendored
@ -1,46 +1,55 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrIncrementOverflow = berror.Error(IncrementOverflow, "this incr invocation will overflow.")
|
||||
ErrDecrementOverflow = berror.Error(DecrementOverflow, "this decr invocation will overflow.")
|
||||
ErrNotIntegerType = berror.Error(NotIntegerType, "item val is not (u)int (u)int32 (u)int64")
|
||||
)
|
||||
|
||||
|
||||
|
||||
func incr(originVal interface{}) (interface{}, error) {
|
||||
switch val := originVal.(type) {
|
||||
case int:
|
||||
tmp := val + 1
|
||||
if val > 0 && tmp < 0 {
|
||||
return nil, fmt.Errorf("increment would overflow")
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return tmp, nil
|
||||
case int32:
|
||||
if val == math.MaxInt32 {
|
||||
return nil, fmt.Errorf("increment would overflow")
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return val + 1, nil
|
||||
case int64:
|
||||
if val == math.MaxInt64 {
|
||||
return nil, fmt.Errorf("increment would overflow")
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return val + 1, nil
|
||||
case uint:
|
||||
tmp := val + 1
|
||||
if tmp < val {
|
||||
return nil, fmt.Errorf("increment would overflow")
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return tmp, nil
|
||||
case uint32:
|
||||
if val == math.MaxUint32 {
|
||||
return nil, fmt.Errorf("increment would overflow")
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return val + 1, nil
|
||||
case uint64:
|
||||
if val == math.MaxUint64 {
|
||||
return nil, fmt.Errorf("increment would overflow")
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return val + 1, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("item val is not (u)int (u)int32 (u)int64")
|
||||
return nil, ErrNotIntegerType
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,35 +58,35 @@ func decr(originVal interface{}) (interface{}, error) {
|
||||
case int:
|
||||
tmp := val - 1
|
||||
if val < 0 && tmp > 0 {
|
||||
return nil, fmt.Errorf("decrement would overflow")
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return tmp, nil
|
||||
case int32:
|
||||
if val == math.MinInt32 {
|
||||
return nil, fmt.Errorf("decrement would overflow")
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return val - 1, nil
|
||||
case int64:
|
||||
if val == math.MinInt64 {
|
||||
return nil, fmt.Errorf("decrement would overflow")
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return val - 1, nil
|
||||
case uint:
|
||||
if val == 0 {
|
||||
return nil, fmt.Errorf("decrement would overflow")
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return val - 1, nil
|
||||
case uint32:
|
||||
if val == 0 {
|
||||
return nil, fmt.Errorf("increment would overflow")
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return val - 1, nil
|
||||
case uint64:
|
||||
if val == 0 {
|
||||
return nil, fmt.Errorf("increment would overflow")
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return val - 1, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("item val is not (u)int (u)int32 (u)int64")
|
||||
return nil, ErrNotIntegerType
|
||||
}
|
||||
}
|
||||
|
||||
71
client/cache/calc_utils_test.go
vendored
71
client/cache/calc_utils_test.go
vendored
@ -14,61 +14,64 @@ func TestIncr(t *testing.T) {
|
||||
var updateVal interface{} = int(2)
|
||||
val, err := incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(int(1<<(strconv.IntSize-1) - 1))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
|
||||
// int32
|
||||
originVal = int32(1)
|
||||
updateVal = int32(2)
|
||||
val, err = incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(int32(math.MaxInt32))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
|
||||
// int64
|
||||
originVal = int64(1)
|
||||
updateVal = int64(2)
|
||||
val, err = incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(int64(math.MaxInt64))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
|
||||
// uint
|
||||
originVal = uint(1)
|
||||
updateVal = uint(2)
|
||||
val, err = incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(uint(1<<(strconv.IntSize) - 1))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
|
||||
// uint32
|
||||
originVal = uint32(1)
|
||||
updateVal = uint32(2)
|
||||
val, err = incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(uint32(math.MaxUint32))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
|
||||
// uint64
|
||||
originVal = uint64(1)
|
||||
updateVal = uint64(2)
|
||||
val, err = incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, updateVal, val)
|
||||
_, err = incr(uint64(math.MaxUint64))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(uint64(math.MaxUint64))
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
// other type
|
||||
_, err = incr("string")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrNotIntegerType, err)
|
||||
}
|
||||
|
||||
func TestDecr(t *testing.T) {
|
||||
@ -77,57 +80,61 @@ func TestDecr(t *testing.T) {
|
||||
var updateVal interface{} = int(1)
|
||||
val, err := decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(int(-1 << (strconv.IntSize - 1)))
|
||||
|
||||
assert.NotNil(t, err)
|
||||
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
// int32
|
||||
originVal = int32(2)
|
||||
updateVal = int32(1)
|
||||
val, err = decr(originVal)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(int32(math.MinInt32))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
|
||||
// int64
|
||||
originVal = int64(2)
|
||||
updateVal = int64(1)
|
||||
val, err = decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(int64(math.MinInt64))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
|
||||
// uint
|
||||
originVal = uint(2)
|
||||
updateVal = uint(1)
|
||||
val, err = decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(uint(0))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
|
||||
// uint32
|
||||
originVal = uint32(2)
|
||||
updateVal = uint32(1)
|
||||
val, err = decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(uint32(0))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
|
||||
// uint64
|
||||
originVal = uint64(2)
|
||||
updateVal = uint64(1)
|
||||
val, err = decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, updateVal, val)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(uint64(0))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
|
||||
// other type
|
||||
_, err = decr("string")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrNotIntegerType, err)
|
||||
}
|
||||
|
||||
182
client/cache/error_code.go
vendored
Normal file
182
client/cache/error_code.go
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright 2021 beego
|
||||
//
|
||||
// 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 (
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
var NilCacheAdapter = berror.DefineCode(4002001, moduleName, "NilCacheAdapter", `
|
||||
It means that you register cache adapter by pass nil.
|
||||
A cache adapter is an instance of Cache interface.
|
||||
`)
|
||||
|
||||
var DuplicateAdapter = berror.DefineCode(4002002, moduleName, "DuplicateAdapter", `
|
||||
You register two adapter with same name. In beego cache module, one name one adapter.
|
||||
Once you got this error, please check the error stack, search adapter
|
||||
`)
|
||||
|
||||
var UnknownAdapter = berror.DefineCode(4002003, moduleName, "UnknownAdapter", `
|
||||
Unknown adapter, do you forget to register the adapter?
|
||||
You must register adapter before use it. For example, if you want to use redis implementation,
|
||||
you must import the cache/redis package.
|
||||
`)
|
||||
|
||||
var IncrementOverflow = berror.DefineCode(4002004, moduleName, "IncrementOverflow", `
|
||||
The increment operation will overflow.
|
||||
`)
|
||||
|
||||
var DecrementOverflow = berror.DefineCode(4002005, moduleName, "DecrementOverflow", `
|
||||
The decrement operation will overflow.
|
||||
`)
|
||||
|
||||
var NotIntegerType = berror.DefineCode(4002006, moduleName, "NotIntegerType", `
|
||||
The type of value is not (u)int (u)int32 (u)int64.
|
||||
When you want to call Incr or Decr function of Cache API, you must confirm that the value's type is one of (u)int (u)int32 (u)int64.
|
||||
`)
|
||||
|
||||
var InvalidFileCacheDirectoryLevelCfg = berror.DefineCode(4002007, moduleName, "InvalidFileCacheDirectoryLevelCfg", `
|
||||
You pass invalid DirectoryLevel parameter when you try to StartAndGC file cache instance.
|
||||
This parameter must be a integer, and please check your input.
|
||||
`)
|
||||
|
||||
var InvalidFileCacheEmbedExpiryCfg = berror.DefineCode(4002008, moduleName, "InvalidFileCacheEmbedExpiryCfg", `
|
||||
You pass invalid EmbedExpiry parameter when you try to StartAndGC file cache instance.
|
||||
This parameter must be a integer, and please check your input.
|
||||
`)
|
||||
|
||||
var CreateFileCacheDirFailed = berror.DefineCode(4002009, moduleName, "CreateFileCacheDirFailed", `
|
||||
Beego failed to create file cache directory. There are two cases:
|
||||
1. You pass invalid CachePath parameter. Please check your input.
|
||||
2. Beego doesn't have the permission to create this directory. Please check your file mode.
|
||||
`)
|
||||
|
||||
var InvalidFileCachePath = berror.DefineCode(4002010, moduleName, "InvalidFilePath", `
|
||||
The file path of FileCache is invalid. Please correct the config.
|
||||
`)
|
||||
|
||||
var ReadFileCacheContentFailed = berror.DefineCode(4002011, moduleName, "ReadFileCacheContentFailed", `
|
||||
Usually you won't got this error. It means that Beego cannot read the data from the file.
|
||||
You need to check whether the file exist. Sometimes it may be deleted by other processes.
|
||||
If the file exists, please check the permission that Beego is able to read data from the file.
|
||||
`)
|
||||
|
||||
var InvalidGobEncodedData = berror.DefineCode(4002012, moduleName, "InvalidEncodedData", `
|
||||
The data is invalid. When you try to decode the invalid data, you got this error.
|
||||
Please confirm that the data is encoded by GOB correctly.
|
||||
`)
|
||||
|
||||
var GobEncodeDataFailed = berror.DefineCode(4002013, moduleName, "GobEncodeDataFailed", `
|
||||
Beego could not encode the data to GOB byte array. In general, the data type is invalid.
|
||||
For example, GOB doesn't support function type.
|
||||
Basic types, string, structure, structure pointer are supported.
|
||||
`)
|
||||
|
||||
var KeyExpired = berror.DefineCode(4002014, moduleName, "KeyExpired", `
|
||||
Cache key is expired.
|
||||
You should notice that, a key is expired and then it may be deleted by GC goroutine.
|
||||
So when you query a key which may be expired, you may got this code, or KeyNotExist.
|
||||
`)
|
||||
|
||||
var KeyNotExist = berror.DefineCode(4002015, moduleName, "KeyNotExist", `
|
||||
Key not found.
|
||||
`)
|
||||
|
||||
var MultiGetFailed = berror.DefineCode(4002016, moduleName, "MultiGetFailed", `
|
||||
Get multiple keys failed. Please check the detail msg to find out the root cause.
|
||||
`)
|
||||
|
||||
var InvalidMemoryCacheCfg = berror.DefineCode(4002017, moduleName, "InvalidMemoryCacheCfg", `
|
||||
The config is invalid. Please check your input. It must be a json string.
|
||||
`)
|
||||
|
||||
var InvalidMemCacheCfg = berror.DefineCode(4002018, moduleName, "InvalidMemCacheCfg", `
|
||||
The config is invalid. Please check your input, it must be json string and contains "conn" field.
|
||||
`)
|
||||
|
||||
var InvalidMemCacheValue = berror.DefineCode(4002019, moduleName, "InvalidMemCacheValue", `
|
||||
The value must be string or byte[], please check your input.
|
||||
`)
|
||||
|
||||
var InvalidRedisCacheCfg = berror.DefineCode(4002020, moduleName, "InvalidRedisCacheCfg", `
|
||||
The config must be json string, and has "conn" field.
|
||||
`)
|
||||
|
||||
var InvalidSsdbCacheCfg = berror.DefineCode(4002021, moduleName, "InvalidSsdbCacheCfg", `
|
||||
The config must be json string, and has "conn" field. The value of "conn" field should be "host:port".
|
||||
"port" must be a valid integer.
|
||||
`)
|
||||
|
||||
var InvalidSsdbCacheValue = berror.DefineCode(4002022, moduleName, "InvalidSsdbCacheValue", `
|
||||
SSDB cache only accept string value. Please check your input.
|
||||
`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var DeleteFileCacheItemFailed = berror.DefineCode(5002001, moduleName, "DeleteFileCacheItemFailed", `
|
||||
Beego try to delete file cache item failed.
|
||||
Please check whether Beego generated file correctly.
|
||||
And then confirm whether this file is already deleted by other processes or other people.
|
||||
`)
|
||||
|
||||
var MemCacheCurdFailed = berror.DefineCode(5002002, moduleName, "MemCacheError", `
|
||||
When you want to get, put, delete key-value from remote memcache servers, you may get error:
|
||||
1. You pass invalid servers address, so Beego could not connect to remote server;
|
||||
2. The servers address is correct, but there is some net issue. Typically there is some firewalls between application and memcache server;
|
||||
3. Key is invalid. The key's length should be less than 250 and must not contains special characters;
|
||||
4. The response from memcache server is invalid;
|
||||
`)
|
||||
|
||||
var RedisCacheCurdFailed = berror.DefineCode(5002003, moduleName, "RedisCacheCurdFailed", `
|
||||
When Beego uses client to send request to redis server, it failed.
|
||||
1. The server addresses is invalid;
|
||||
2. Network issue, firewall issue or network is unstable;
|
||||
3. Client failed to manage connection. In extreme cases, Beego's redis client didn't maintain connections correctly, for example, Beego try to send request via closed connection;
|
||||
4. The request are huge and redis server spent too much time to process it, and client is timeout;
|
||||
|
||||
In general, if you always got this error whatever you do, in most cases, it was caused by network issue.
|
||||
You could check your network state, and confirm that firewall rules are correct.
|
||||
`)
|
||||
|
||||
var InvalidConnection = berror.DefineCode(5002004, moduleName, "InvalidConnection", `
|
||||
The connection is invalid. Please check your connection info, network, firewall.
|
||||
You could simply uses ping, telnet or write some simple tests to test network.
|
||||
`)
|
||||
|
||||
var DialFailed = berror.DefineCode(5002005, moduleName, "DialFailed", `
|
||||
When Beego try to dial to remote servers, it failed. Please check your connection info and network state, server state.
|
||||
`)
|
||||
|
||||
var SsdbCacheCurdFailed = berror.DefineCode(5002006, moduleName, "SsdbCacheCurdFailed", `
|
||||
When you try to use SSDB cache, it failed. There are many cases:
|
||||
1. servers unavailable;
|
||||
2. network issue, including network unstable, firewall;
|
||||
3. connection issue;
|
||||
4. request are huge and servers spent too much time to process it, got timeout;
|
||||
`)
|
||||
|
||||
var SsdbBadResponse = berror.DefineCode(5002007, moduleName, "SsdbBadResponse", `
|
||||
The reponse from SSDB server is invalid.
|
||||
Usually it indicates something wrong on server side.
|
||||
`)
|
||||
|
||||
var ErrKeyExpired = berror.Error(KeyExpired, "the key is expired")
|
||||
var ErrKeyNotExist = berror.Error(KeyNotExist, "the key isn't exist")
|
||||
143
client/cache/file.go
vendored
143
client/cache/file.go
vendored
@ -30,7 +30,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
// FileCacheItem is basic unit of file cache adapter which
|
||||
@ -73,38 +73,60 @@ func (fc *FileCache) StartAndGC(config string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := cfg["CachePath"]; !ok {
|
||||
cfg["CachePath"] = FileCachePath
|
||||
}
|
||||
if _, ok := cfg["FileSuffix"]; !ok {
|
||||
cfg["FileSuffix"] = FileCacheFileSuffix
|
||||
}
|
||||
if _, ok := cfg["DirectoryLevel"]; !ok {
|
||||
cfg["DirectoryLevel"] = strconv.Itoa(FileCacheDirectoryLevel)
|
||||
}
|
||||
if _, ok := cfg["EmbedExpiry"]; !ok {
|
||||
cfg["EmbedExpiry"] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10)
|
||||
}
|
||||
fc.CachePath = cfg["CachePath"]
|
||||
fc.FileSuffix = cfg["FileSuffix"]
|
||||
fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"])
|
||||
fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"])
|
||||
|
||||
fc.Init()
|
||||
return nil
|
||||
const cpKey = "CachePath"
|
||||
const fsKey = "FileSuffix"
|
||||
const dlKey = "DirectoryLevel"
|
||||
const eeKey = "EmbedExpiry"
|
||||
|
||||
if _, ok := cfg[cpKey]; !ok {
|
||||
cfg[cpKey] = FileCachePath
|
||||
}
|
||||
|
||||
if _, ok := cfg[fsKey]; !ok {
|
||||
cfg[fsKey] = FileCacheFileSuffix
|
||||
}
|
||||
|
||||
if _, ok := cfg[dlKey]; !ok {
|
||||
cfg[dlKey] = strconv.Itoa(FileCacheDirectoryLevel)
|
||||
}
|
||||
|
||||
if _, ok := cfg[eeKey]; !ok {
|
||||
cfg[eeKey] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10)
|
||||
}
|
||||
fc.CachePath = cfg[cpKey]
|
||||
fc.FileSuffix = cfg[fsKey]
|
||||
fc.DirectoryLevel, err = strconv.Atoi(cfg[dlKey])
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, InvalidFileCacheDirectoryLevelCfg,
|
||||
"invalid directory level config, please check your input, it must be integer: %s", cfg[dlKey])
|
||||
}
|
||||
fc.EmbedExpiry, err = strconv.Atoi(cfg[eeKey])
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, InvalidFileCacheEmbedExpiryCfg,
|
||||
"invalid embed expiry config, please check your input, it must be integer: %s", cfg[eeKey])
|
||||
}
|
||||
return fc.Init()
|
||||
}
|
||||
|
||||
// Init makes new a dir for file cache if it does not already exist
|
||||
func (fc *FileCache) Init() {
|
||||
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
|
||||
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
|
||||
func (fc *FileCache) Init() error {
|
||||
ok, err := exists(fc.CachePath)
|
||||
if err != nil || ok {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(fc.CachePath, os.ModePerm)
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, CreateFileCacheDirFailed,
|
||||
"could not create directory, please check the config [%s] and file mode.", fc.CachePath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getCachedFilename returns an md5 encoded file name.
|
||||
func (fc *FileCache) getCacheFileName(key string) string {
|
||||
func (fc *FileCache) getCacheFileName(key string) (string, error) {
|
||||
m := md5.New()
|
||||
io.WriteString(m, key)
|
||||
_, _ = io.WriteString(m, key)
|
||||
keyMd5 := hex.EncodeToString(m.Sum(nil))
|
||||
cachePath := fc.CachePath
|
||||
switch fc.DirectoryLevel {
|
||||
@ -113,18 +135,29 @@ func (fc *FileCache) getCacheFileName(key string) string {
|
||||
case 1:
|
||||
cachePath = filepath.Join(cachePath, keyMd5[0:2])
|
||||
}
|
||||
|
||||
if ok, _ := exists(cachePath); !ok { // todo : error handle
|
||||
_ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle
|
||||
ok, err := exists(cachePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !ok {
|
||||
err = os.MkdirAll(cachePath, os.ModePerm)
|
||||
if err != nil {
|
||||
return "", berror.Wrapf(err, CreateFileCacheDirFailed,
|
||||
"could not create the directory: %s", cachePath)
|
||||
}
|
||||
}
|
||||
|
||||
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix))
|
||||
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix)), nil
|
||||
}
|
||||
|
||||
// Get value from file cache.
|
||||
// if nonexistent or expired return an empty string.
|
||||
func (fc *FileCache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
fileData, err := FileGetContents(fc.getCacheFileName(key))
|
||||
fn, err := fc.getCacheFileName(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileData, err := FileGetContents(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -136,7 +169,7 @@ func (fc *FileCache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
}
|
||||
|
||||
if to.Expired.Before(time.Now()) {
|
||||
return nil, errors.New("The key is expired")
|
||||
return nil, ErrKeyExpired
|
||||
}
|
||||
return to.Data, nil
|
||||
}
|
||||
@ -159,7 +192,7 @@ func (fc *FileCache) GetMulti(ctx context.Context, keys []string) ([]interface{}
|
||||
if len(keysErr) == 0 {
|
||||
return rc, nil
|
||||
}
|
||||
return rc, errors.New(strings.Join(keysErr, "; "))
|
||||
return rc, berror.Error(MultiGetFailed, strings.Join(keysErr, "; "))
|
||||
}
|
||||
|
||||
// Put value into file cache.
|
||||
@ -179,14 +212,26 @@ func (fc *FileCache) Put(ctx context.Context, key string, val interface{}, timeo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return FilePutContents(fc.getCacheFileName(key), data)
|
||||
|
||||
fn, err := fc.getCacheFileName(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return FilePutContents(fn, data)
|
||||
}
|
||||
|
||||
// Delete file cache value.
|
||||
func (fc *FileCache) Delete(ctx context.Context, key string) error {
|
||||
filename := fc.getCacheFileName(key)
|
||||
filename, err := fc.getCacheFileName(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok, _ := exists(filename); ok {
|
||||
return os.Remove(filename)
|
||||
err = os.Remove(filename)
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, DeleteFileCacheItemFailed,
|
||||
"can not delete this file cache key-value, key is %s and file name is %s", key, filename)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -224,8 +269,11 @@ func (fc *FileCache) Decr(ctx context.Context, key string) error {
|
||||
|
||||
// IsExist checks if value exists.
|
||||
func (fc *FileCache) IsExist(ctx context.Context, key string) (bool, error) {
|
||||
ret, _ := exists(fc.getCacheFileName(key))
|
||||
return ret, nil
|
||||
fn, err := fc.getCacheFileName(key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return exists(fn)
|
||||
}
|
||||
|
||||
// ClearAll cleans cached files (not implemented)
|
||||
@ -242,13 +290,19 @@ func exists(path string) (bool, error) {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
return false, berror.Wrapf(err, InvalidFileCachePath, "file cache path is invalid: %s", path)
|
||||
}
|
||||
|
||||
// FileGetContents Reads bytes from a file.
|
||||
// if non-existent, create this file.
|
||||
func FileGetContents(filename string) (data []byte, e error) {
|
||||
return ioutil.ReadFile(filename)
|
||||
func FileGetContents(filename string) ([]byte, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, berror.Wrapf(err, ReadFileCacheContentFailed,
|
||||
"could not read the data from the file: %s, " +
|
||||
"please confirm that file exist and Beego has the permission to read the content.", filename)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// FilePutContents puts bytes into a file.
|
||||
@ -263,16 +317,21 @@ func GobEncode(data interface{}) ([]byte, error) {
|
||||
enc := gob.NewEncoder(buf)
|
||||
err := enc.Encode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, berror.Wrap(err, GobEncodeDataFailed, "could not encode this data")
|
||||
}
|
||||
return buf.Bytes(), err
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// GobDecode Gob decodes a file cache item.
|
||||
func GobDecode(data []byte, to *FileCacheItem) error {
|
||||
buf := bytes.NewBuffer(data)
|
||||
dec := gob.NewDecoder(buf)
|
||||
return dec.Decode(&to)
|
||||
err := dec.Decode(&to)
|
||||
if err != nil {
|
||||
return berror.Wrap(err, InvalidGobEncodedData,
|
||||
"could not decode this data to FileCacheItem. Make sure that the data is encoded by GOB.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
108
client/cache/file_test.go
vendored
Normal file
108
client/cache/file_test.go
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2021 beego
|
||||
//
|
||||
// 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"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFileCacheStartAndGC(t *testing.T) {
|
||||
fc := NewFileCache().(*FileCache)
|
||||
err := fc.StartAndGC(`{`)
|
||||
assert.NotNil(t, err)
|
||||
err = fc.StartAndGC(`{}`)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, fc.CachePath, FileCachePath)
|
||||
assert.Equal(t, fc.DirectoryLevel, FileCacheDirectoryLevel)
|
||||
assert.Equal(t, fc.EmbedExpiry, int(FileCacheEmbedExpiry))
|
||||
assert.Equal(t, fc.FileSuffix, FileCacheFileSuffix)
|
||||
|
||||
err = fc.StartAndGC(`{"CachePath":"/cache","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"0"}`)
|
||||
// could not create dir
|
||||
assert.NotNil(t, err)
|
||||
|
||||
str := getTestCacheFilePath()
|
||||
err = fc.StartAndGC(fmt.Sprintf(`{"CachePath":"%s","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"0"}`, str))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, fc.CachePath, str)
|
||||
assert.Equal(t, fc.DirectoryLevel, 2)
|
||||
assert.Equal(t, fc.EmbedExpiry, 0)
|
||||
assert.Equal(t, fc.FileSuffix, ".bin")
|
||||
|
||||
err = fc.StartAndGC(fmt.Sprintf(`{"CachePath":"%s","FileSuffix":".bin","DirectoryLevel":"aaa","EmbedExpiry":"0"}`, str))
|
||||
assert.NotNil(t, err)
|
||||
|
||||
err = fc.StartAndGC(fmt.Sprintf(`{"CachePath":"%s","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"aaa"}`, str))
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestFileCacheInit(t *testing.T) {
|
||||
fc := NewFileCache().(*FileCache)
|
||||
fc.CachePath = "////aaa"
|
||||
err := fc.Init()
|
||||
assert.NotNil(t, err)
|
||||
fc.CachePath = getTestCacheFilePath()
|
||||
err = fc.Init()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestFileGetContents(t *testing.T) {
|
||||
_, err := FileGetContents("/bin/aaa")
|
||||
assert.NotNil(t, err)
|
||||
fn := filepath.Join(os.TempDir(), "fileCache.txt")
|
||||
f, err := os.Create(fn)
|
||||
assert.Nil(t, err)
|
||||
_, err = f.WriteString("text")
|
||||
assert.Nil(t, err)
|
||||
data, err := FileGetContents(fn)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "text", string(data))
|
||||
}
|
||||
|
||||
func TestGobEncodeDecode(t *testing.T) {
|
||||
_, err := GobEncode(func() {
|
||||
fmt.Print("test func")
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
data, err := GobEncode(&FileCacheItem{
|
||||
Data: "hello",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
err = GobDecode([]byte("wrong data"), &FileCacheItem{})
|
||||
assert.NotNil(t, err)
|
||||
dci := &FileCacheItem{}
|
||||
err = GobDecode(data, dci)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "hello", dci.Data)
|
||||
}
|
||||
|
||||
func TestFileCacheDelete(t *testing.T) {
|
||||
fc := NewFileCache()
|
||||
err := fc.StartAndGC(`{}`)
|
||||
assert.Nil(t, err)
|
||||
err = fc.Delete(context.Background(), "my-key")
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func getTestCacheFilePath() string {
|
||||
return filepath.Join(os.TempDir(), "test", "file.txt")
|
||||
}
|
||||
91
client/cache/memcache/memcache.go
vendored
91
client/cache/memcache/memcache.go
vendored
@ -32,7 +32,6 @@ package memcache
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@ -40,6 +39,7 @@ import (
|
||||
"github.com/bradfitz/gomemcache/memcache"
|
||||
|
||||
"github.com/beego/beego/v2/client/cache"
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
// Cache Memcache adapter.
|
||||
@ -55,30 +55,25 @@ func NewMemCache() cache.Cache {
|
||||
|
||||
// Get get value from memcache.
|
||||
func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if item, err := rc.conn.Get(key); err == nil {
|
||||
return item.Value, nil
|
||||
} else {
|
||||
return nil, err
|
||||
return nil, berror.Wrapf(err, cache.MemCacheCurdFailed,
|
||||
"could not read data from memcache, please check your key, network and connection. Root cause: %s",
|
||||
err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// GetMulti gets a value from a key in memcache.
|
||||
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
||||
rv := make([]interface{}, len(keys))
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return rv, err
|
||||
}
|
||||
}
|
||||
|
||||
mv, err := rc.conn.GetMulti(keys)
|
||||
if err != nil {
|
||||
return rv, err
|
||||
return rv, berror.Wrapf(err, cache.MemCacheCurdFailed,
|
||||
"could not read multiple key-values from memcache, " +
|
||||
"please check your keys, network and connection. Root cause: %s",
|
||||
err.Error())
|
||||
}
|
||||
|
||||
keysErr := make([]string, 0)
|
||||
@ -93,78 +88,54 @@ func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, er
|
||||
if len(keysErr) == 0 {
|
||||
return rv, nil
|
||||
}
|
||||
return rv, fmt.Errorf(strings.Join(keysErr, "; "))
|
||||
return rv, berror.Error(cache.MultiGetFailed, strings.Join(keysErr, "; "))
|
||||
}
|
||||
|
||||
// Put puts a value into memcache.
|
||||
func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
item := memcache.Item{Key: key, Expiration: int32(timeout / time.Second)}
|
||||
if v, ok := val.([]byte); ok {
|
||||
item.Value = v
|
||||
} else if str, ok := val.(string); ok {
|
||||
item.Value = []byte(str)
|
||||
} else {
|
||||
return errors.New("val only support string and []byte")
|
||||
return berror.Errorf(cache.InvalidMemCacheValue,
|
||||
"the value must be string or byte[]. key: %s, value:%v", key, val)
|
||||
}
|
||||
return rc.conn.Set(&item)
|
||||
return berror.Wrapf(rc.conn.Set(&item), cache.MemCacheCurdFailed,
|
||||
"could not put key-value to memcache, key: %s", key)
|
||||
}
|
||||
|
||||
// Delete deletes a value in memcache.
|
||||
func (rc *Cache) Delete(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return rc.conn.Delete(key)
|
||||
return berror.Wrapf(rc.conn.Delete(key), cache.MemCacheCurdFailed,
|
||||
"could not delete key-value from memcache, key: %s", key)
|
||||
}
|
||||
|
||||
// Incr increases counter.
|
||||
func (rc *Cache) Incr(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Increment(key, 1)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.MemCacheCurdFailed,
|
||||
"could not increase value for key: %s", key)
|
||||
}
|
||||
|
||||
// Decr decreases counter.
|
||||
func (rc *Cache) Decr(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Decrement(key, 1)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.MemCacheCurdFailed,
|
||||
"could not decrease value for key: %s", key)
|
||||
}
|
||||
|
||||
// IsExist checks if a value exists in memcache.
|
||||
func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Get(key)
|
||||
_, err := rc.Get(ctx, key)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
// ClearAll clears all cache in memcache.
|
||||
func (rc *Cache) ClearAll(context.Context) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return rc.conn.FlushAll()
|
||||
return berror.Wrap(rc.conn.FlushAll(), cache.MemCacheCurdFailed,
|
||||
"try to clear all key-value pairs failed")
|
||||
}
|
||||
|
||||
// StartAndGC starts the memcache adapter.
|
||||
@ -172,21 +143,15 @@ func (rc *Cache) ClearAll(context.Context) error {
|
||||
// If an error occurs during connecting, an error is returned
|
||||
func (rc *Cache) StartAndGC(config string) error {
|
||||
var cf map[string]string
|
||||
json.Unmarshal([]byte(config), &cf)
|
||||
if err := json.Unmarshal([]byte(config), &cf); err != nil {
|
||||
return berror.Wrapf(err, cache.InvalidMemCacheCfg,
|
||||
"could not unmarshal this config, it must be valid json stringP: %s", config)
|
||||
}
|
||||
|
||||
if _, ok := cf["conn"]; !ok {
|
||||
return errors.New("config has no conn key")
|
||||
return berror.Errorf(cache.InvalidMemCacheCfg, `config must contains "conn" field: %s`, config)
|
||||
}
|
||||
rc.conninfo = strings.Split(cf["conn"], ";")
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// connect to memcache and keep the connection.
|
||||
func (rc *Cache) connectInit() error {
|
||||
rc.conn = memcache.New(rc.conninfo...)
|
||||
return nil
|
||||
}
|
||||
|
||||
3
client/cache/memcache/memcache_test.go
vendored
3
client/cache/memcache/memcache_test.go
vendored
@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -106,7 +107,7 @@ func TestMemcacheCache(t *testing.T) {
|
||||
assert.Equal(t, "author1", string(vv[1].([]byte)))
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "key [astaxie0] error: key not exist", err.Error())
|
||||
assert.True(t, strings.Contains(err.Error(), "key not exist"))
|
||||
|
||||
assert.Nil(t, bm.ClearAll(context.Background()))
|
||||
// test clear all
|
||||
|
||||
30
client/cache/memory.go
vendored
30
client/cache/memory.go
vendored
@ -17,11 +17,12 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -29,8 +30,6 @@ var (
|
||||
DefaultEvery = 60 // 1 minute
|
||||
)
|
||||
|
||||
const keyNotExistMsg = "key not exist"
|
||||
|
||||
// MemoryItem stores memory cache item.
|
||||
type MemoryItem struct {
|
||||
val interface{}
|
||||
@ -66,13 +65,14 @@ func NewMemoryCache() Cache {
|
||||
func (bc *MemoryCache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
bc.RLock()
|
||||
defer bc.RUnlock()
|
||||
if itm, ok := bc.items[key]; ok {
|
||||
if itm, ok :=
|
||||
bc.items[key]; ok {
|
||||
if itm.isExpire() {
|
||||
return nil, errors.New("the key is expired")
|
||||
return nil, ErrKeyExpired
|
||||
}
|
||||
return itm.val, nil
|
||||
}
|
||||
return nil, errors.New(keyNotExistMsg)
|
||||
return nil, ErrKeyNotExist
|
||||
}
|
||||
|
||||
// GetMulti gets caches from memory.
|
||||
@ -93,7 +93,7 @@ func (bc *MemoryCache) GetMulti(ctx context.Context, keys []string) ([]interface
|
||||
if len(keysErr) == 0 {
|
||||
return rc, nil
|
||||
}
|
||||
return rc, errors.New(strings.Join(keysErr, "; "))
|
||||
return rc, berror.Error(MultiGetFailed, strings.Join(keysErr, "; "))
|
||||
}
|
||||
|
||||
// Put puts cache into memory.
|
||||
@ -110,17 +110,11 @@ func (bc *MemoryCache) Put(ctx context.Context, key string, val interface{}, tim
|
||||
}
|
||||
|
||||
// Delete cache in memory.
|
||||
// If the key is not found, it will not return error
|
||||
func (bc *MemoryCache) Delete(ctx context.Context, key string) error {
|
||||
bc.Lock()
|
||||
defer bc.Unlock()
|
||||
if _, ok := bc.items[key]; !ok {
|
||||
|
||||
return errors.New(keyNotExistMsg)
|
||||
}
|
||||
delete(bc.items, key)
|
||||
if _, ok := bc.items[key]; ok {
|
||||
return errors.New("delete key error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -131,7 +125,7 @@ func (bc *MemoryCache) Incr(ctx context.Context, key string) error {
|
||||
defer bc.Unlock()
|
||||
itm, ok := bc.items[key]
|
||||
if !ok {
|
||||
return errors.New(keyNotExistMsg)
|
||||
return ErrKeyNotExist
|
||||
}
|
||||
|
||||
val, err := incr(itm.val)
|
||||
@ -148,7 +142,7 @@ func (bc *MemoryCache) Decr(ctx context.Context, key string) error {
|
||||
defer bc.Unlock()
|
||||
itm, ok := bc.items[key]
|
||||
if !ok {
|
||||
return errors.New(keyNotExistMsg)
|
||||
return ErrKeyNotExist
|
||||
}
|
||||
|
||||
val, err := decr(itm.val)
|
||||
@ -180,7 +174,9 @@ func (bc *MemoryCache) ClearAll(context.Context) error {
|
||||
// StartAndGC starts memory cache. Checks expiration in every clock time.
|
||||
func (bc *MemoryCache) StartAndGC(config string) error {
|
||||
var cf map[string]int
|
||||
json.Unmarshal([]byte(config), &cf)
|
||||
if err := json.Unmarshal([]byte(config), &cf); err != nil {
|
||||
return berror.Wrapf(err, InvalidMemoryCacheCfg, "invalid config, please check your input: %s", config)
|
||||
}
|
||||
if _, ok := cf["interval"]; !ok {
|
||||
cf = make(map[string]int)
|
||||
cf["interval"] = DefaultEvery
|
||||
|
||||
17
client/cache/module.go
vendored
Normal file
17
client/cache/module.go
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2021 beego
|
||||
//
|
||||
// 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
|
||||
|
||||
const moduleName = "cache"
|
||||
59
client/cache/redis/redis.go
vendored
59
client/cache/redis/redis.go
vendored
@ -32,7 +32,6 @@ package redis
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -41,6 +40,7 @@ import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
|
||||
"github.com/beego/beego/v2/client/cache"
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -67,15 +67,20 @@ func NewRedisCache() cache.Cache {
|
||||
}
|
||||
|
||||
// Execute the redis commands. args[0] must be the key name
|
||||
func (rc *Cache) do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
||||
if len(args) < 1 {
|
||||
return nil, errors.New("missing required arguments")
|
||||
}
|
||||
func (rc *Cache) do(commandName string, args ...interface{}) (interface{}, error) {
|
||||
args[0] = rc.associate(args[0])
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
|
||||
return c.Do(commandName, args...)
|
||||
reply, err := c.Do(commandName, args...)
|
||||
if err != nil {
|
||||
return nil, berror.Wrapf(err, cache.RedisCacheCurdFailed,
|
||||
"could not execute this command: %s", commandName)
|
||||
}
|
||||
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
// associate with config key.
|
||||
@ -95,7 +100,9 @@ func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
// GetMulti gets cache from redis.
|
||||
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
var args []interface{}
|
||||
for _, key := range keys {
|
||||
args = append(args, rc.associate(key))
|
||||
@ -137,13 +144,16 @@ func (rc *Cache) Decr(ctx context.Context, key string) error {
|
||||
}
|
||||
|
||||
// ClearAll deletes all cache in the redis collection
|
||||
// Be careful about this method, because it scans all keys and the delete them one by one
|
||||
func (rc *Cache) ClearAll(context.Context) error {
|
||||
cachedKeys, err := rc.Scan(rc.key + ":*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
for _, str := range cachedKeys {
|
||||
if _, err = c.Do("DEL", str); err != nil {
|
||||
return err
|
||||
@ -155,7 +165,9 @@ func (rc *Cache) ClearAll(context.Context) error {
|
||||
// Scan scans all keys matching a given pattern.
|
||||
func (rc *Cache) Scan(pattern string) (keys []string, err error) {
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
var (
|
||||
cursor uint64 = 0 // start
|
||||
result []interface{}
|
||||
@ -186,13 +198,16 @@ func (rc *Cache) Scan(pattern string) (keys []string, err error) {
|
||||
// Cached items in redis are stored forever, no garbage collection happens
|
||||
func (rc *Cache) StartAndGC(config string) error {
|
||||
var cf map[string]string
|
||||
json.Unmarshal([]byte(config), &cf)
|
||||
err := json.Unmarshal([]byte(config), &cf)
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, cache.InvalidRedisCacheCfg, "could not unmarshal the config: %s", config)
|
||||
}
|
||||
|
||||
if _, ok := cf["key"]; !ok {
|
||||
cf["key"] = DefaultKey
|
||||
}
|
||||
if _, ok := cf["conn"]; !ok {
|
||||
return errors.New("config has no conn key")
|
||||
return berror.Wrapf(err, cache.InvalidRedisCacheCfg, "config missing conn field: %s", config)
|
||||
}
|
||||
|
||||
// Format redis://<password>@<host>:<port>
|
||||
@ -229,9 +244,16 @@ func (rc *Cache) StartAndGC(config string) error {
|
||||
rc.connectInit()
|
||||
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
|
||||
return c.Err()
|
||||
// test connection
|
||||
if err = c.Err(); err != nil {
|
||||
return berror.Wrapf(err, cache.InvalidConnection,
|
||||
"can not connect to remote redis server, please check the connection info and network state: %s", config)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// connect to redis.
|
||||
@ -239,19 +261,20 @@ func (rc *Cache) connectInit() {
|
||||
dialFunc := func() (c redis.Conn, err error) {
|
||||
c, err = redis.Dial("tcp", rc.conninfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, berror.Wrapf(err, cache.DialFailed,
|
||||
"could not dial to remote server: %s ", rc.conninfo)
|
||||
}
|
||||
|
||||
if rc.password != "" {
|
||||
if _, err := c.Do("AUTH", rc.password); err != nil {
|
||||
c.Close()
|
||||
if _, err = c.Do("AUTH", rc.password); err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
_, selecterr := c.Do("SELECT", rc.dbNum)
|
||||
if selecterr != nil {
|
||||
c.Close()
|
||||
_ = c.Close()
|
||||
return nil, selecterr
|
||||
}
|
||||
return
|
||||
|
||||
109
client/cache/ssdb/ssdb.go
vendored
109
client/cache/ssdb/ssdb.go
vendored
@ -3,7 +3,6 @@ package ssdb
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -12,6 +11,7 @@ import (
|
||||
"github.com/ssdb/gossdb/ssdb"
|
||||
|
||||
"github.com/beego/beego/v2/client/cache"
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
// Cache SSDB adapter
|
||||
@ -27,31 +27,21 @@ func NewSsdbCache() cache.Cache {
|
||||
|
||||
// Get gets a key's value from memcache.
|
||||
func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
value, err := rc.conn.Get(key)
|
||||
if err == nil {
|
||||
return value, nil
|
||||
}
|
||||
return nil, err
|
||||
return nil, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "could not get value, key: %s", key)
|
||||
}
|
||||
|
||||
// GetMulti gets one or keys values from ssdb.
|
||||
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
||||
size := len(keys)
|
||||
values := make([]interface{}, size)
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return values, err
|
||||
}
|
||||
}
|
||||
|
||||
res, err := rc.conn.Do("multi_get", keys)
|
||||
if err != nil {
|
||||
return values, err
|
||||
return values, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "multi_get failed, key: %v", keys)
|
||||
}
|
||||
|
||||
resSize := len(res)
|
||||
@ -70,7 +60,7 @@ func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, er
|
||||
}
|
||||
|
||||
if len(keysErr) != 0 {
|
||||
return values, fmt.Errorf(strings.Join(keysErr, "; "))
|
||||
return values, berror.Error(cache.MultiGetFailed, strings.Join(keysErr, "; "))
|
||||
}
|
||||
|
||||
return values, nil
|
||||
@ -78,26 +68,16 @@ func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, er
|
||||
|
||||
// DelMulti deletes one or more keys from memcache
|
||||
func (rc *Cache) DelMulti(keys []string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Do("multi_del", keys)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "multi_del failed: %v", keys)
|
||||
}
|
||||
|
||||
// Put puts value into memcache.
|
||||
// value: must be of type string
|
||||
func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
v, ok := val.(string)
|
||||
if !ok {
|
||||
return errors.New("value must string")
|
||||
return berror.Errorf(cache.InvalidSsdbCacheValue, "value must be string: %v", val)
|
||||
}
|
||||
var resp []string
|
||||
var err error
|
||||
@ -108,57 +88,37 @@ func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout t
|
||||
resp, err = rc.conn.Do("setx", key, v, ttl)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "set or setx failed, key: %s", key)
|
||||
}
|
||||
if len(resp) == 2 && resp[0] == "ok" {
|
||||
return nil
|
||||
}
|
||||
return errors.New("bad response")
|
||||
return berror.Errorf(cache.SsdbBadResponse, "the response from SSDB server is invalid: %v", resp)
|
||||
}
|
||||
|
||||
// Delete deletes a value in memcache.
|
||||
func (rc *Cache) Delete(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Del(key)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "del failed: %s", key)
|
||||
}
|
||||
|
||||
// Incr increases a key's counter.
|
||||
func (rc *Cache) Incr(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Do("incr", key, 1)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "increase failed: %s", key)
|
||||
}
|
||||
|
||||
// Decr decrements a key's counter.
|
||||
func (rc *Cache) Decr(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Do("incr", key, -1)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "decrease failed: %s", key)
|
||||
}
|
||||
|
||||
// IsExist checks if a key exists in memcache.
|
||||
func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
resp, err := rc.conn.Do("exists", key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "exists failed: %s", key)
|
||||
}
|
||||
if len(resp) == 2 && resp[1] == "1" {
|
||||
return true, nil
|
||||
@ -167,13 +127,9 @@ func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) {
|
||||
|
||||
}
|
||||
|
||||
// ClearAll clears all cached items in memcache.
|
||||
// ClearAll clears all cached items in ssdb.
|
||||
// If there are many keys, this method may spent much time.
|
||||
func (rc *Cache) ClearAll(context.Context) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
keyStart, keyEnd, limit := "", "", 50
|
||||
resp, err := rc.Scan(keyStart, keyEnd, limit)
|
||||
for err == nil {
|
||||
@ -187,21 +143,16 @@ func (rc *Cache) ClearAll(context.Context) error {
|
||||
}
|
||||
_, e := rc.conn.Do("multi_del", keys)
|
||||
if e != nil {
|
||||
return e
|
||||
return berror.Wrapf(e, cache.SsdbCacheCurdFailed, "multi_del failed: %v", keys)
|
||||
}
|
||||
keyStart = resp[size-2]
|
||||
resp, err = rc.Scan(keyStart, keyEnd, limit)
|
||||
}
|
||||
return err
|
||||
return berror.Wrap(err, cache.SsdbCacheCurdFailed, "scan failed")
|
||||
}
|
||||
|
||||
// Scan key all cached in ssdb.
|
||||
func (rc *Cache) Scan(keyStart string, keyEnd string, limit int) ([]string, error) {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
resp, err := rc.conn.Do("scan", keyStart, keyEnd, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -214,30 +165,36 @@ func (rc *Cache) Scan(keyStart string, keyEnd string, limit int) ([]string, erro
|
||||
// If an error occurs during connection, an error is returned
|
||||
func (rc *Cache) StartAndGC(config string) error {
|
||||
var cf map[string]string
|
||||
json.Unmarshal([]byte(config), &cf)
|
||||
err := json.Unmarshal([]byte(config), &cf)
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, cache.InvalidSsdbCacheCfg,
|
||||
"unmarshal this config failed, it must be a valid json string: %s", config)
|
||||
}
|
||||
if _, ok := cf["conn"]; !ok {
|
||||
return errors.New("config has no conn key")
|
||||
return berror.Wrapf(err, cache.InvalidSsdbCacheCfg,
|
||||
"Missing conn field: %s", config)
|
||||
}
|
||||
rc.conninfo = strings.Split(cf["conn"], ";")
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return rc.connectInit()
|
||||
}
|
||||
|
||||
// connect to memcache and keep the connection.
|
||||
func (rc *Cache) connectInit() error {
|
||||
conninfoArray := strings.Split(rc.conninfo[0], ":")
|
||||
if len(conninfoArray) < 2 {
|
||||
return berror.Errorf(cache.InvalidSsdbCacheCfg, "The value of conn should be host:port: %s", rc.conninfo[0])
|
||||
}
|
||||
host := conninfoArray[0]
|
||||
port, e := strconv.Atoi(conninfoArray[1])
|
||||
if e != nil {
|
||||
return e
|
||||
return berror.Errorf(cache.InvalidSsdbCacheCfg, "Port is invalid. It must be integer, %s", rc.conninfo[0])
|
||||
}
|
||||
var err error
|
||||
rc.conn, err = ssdb.Connect(host, port)
|
||||
return err
|
||||
if rc.conn, err = ssdb.Connect(host, port); err != nil {
|
||||
return berror.Wrapf(err, cache.InvalidConnection,
|
||||
"could not connect to SSDB, please check your connection info, network and firewall: %s", rc.conninfo[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
3
client/cache/ssdb/ssdb_test.go
vendored
3
client/cache/ssdb/ssdb_test.go
vendored
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -89,7 +90,7 @@ func TestSsdbcacheCache(t *testing.T) {
|
||||
assert.Nil(t, vv[1])
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "key [ssdb11] error: key not exist", err.Error())
|
||||
assert.True(t, strings.Contains(err.Error(), "key not exist"))
|
||||
|
||||
// test clear all done
|
||||
assert.Nil(t, ssdb.ClearAll(context.Background()))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user