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
|
# 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)
|
- 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)
|
- 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)
|
- ORM mock. [4407](https://github.com/beego/beego/pull/4407)
|
||||||
|
|||||||
@ -1 +1,5 @@
|
|||||||
# Error Module
|
# 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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/beego/beego/v2/core/berror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cache interface contains all behaviors for cache adapter.
|
// Cache interface contains all behaviors for cache adapter.
|
||||||
@ -55,12 +56,14 @@ type Cache interface {
|
|||||||
// Set a cached value with key and expire time.
|
// Set a cached value with key and expire time.
|
||||||
Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error
|
Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error
|
||||||
// Delete cached value by key.
|
// Delete cached value by key.
|
||||||
|
// Should not return error if key not found
|
||||||
Delete(ctx context.Context, key string) error
|
Delete(ctx context.Context, key string) error
|
||||||
// Increment a cached int value by key, as a counter.
|
// Increment a cached int value by key, as a counter.
|
||||||
Incr(ctx context.Context, key string) error
|
Incr(ctx context.Context, key string) error
|
||||||
// Decrement a cached int value by key, as a counter.
|
// Decrement a cached int value by key, as a counter.
|
||||||
Decr(ctx context.Context, key string) error
|
Decr(ctx context.Context, key string) error
|
||||||
// Check if a cached value exists or not.
|
// Check if a cached value exists or not.
|
||||||
|
// if key is expired, return (false, nil)
|
||||||
IsExist(ctx context.Context, key string) (bool, error)
|
IsExist(ctx context.Context, key string) (bool, error)
|
||||||
// Clear all cache.
|
// Clear all cache.
|
||||||
ClearAll(ctx context.Context) error
|
ClearAll(ctx context.Context) error
|
||||||
@ -78,7 +81,7 @@ var adapters = make(map[string]Instance)
|
|||||||
// it panics.
|
// it panics.
|
||||||
func Register(name string, adapter Instance) {
|
func Register(name string, adapter Instance) {
|
||||||
if adapter == nil {
|
if adapter == nil {
|
||||||
panic("cache: Register adapter is nil")
|
panic(berror.Error(NilCacheAdapter, "cache: Register adapter is nil").Error())
|
||||||
}
|
}
|
||||||
if _, ok := adapters[name]; ok {
|
if _, ok := adapters[name]; ok {
|
||||||
panic("cache: Register called twice for adapter " + name)
|
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) {
|
func NewCache(adapterName, config string) (adapter Cache, err error) {
|
||||||
instanceFunc, ok := adapters[adapterName]
|
instanceFunc, ok := adapters[adapterName]
|
||||||
if !ok {
|
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
|
return
|
||||||
}
|
}
|
||||||
adapter = instanceFunc()
|
adapter = instanceFunc()
|
||||||
|
|||||||
3
client/cache/cache_test.go
vendored
3
client/cache/cache_test.go
vendored
@ -18,6 +18,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -108,7 +109,7 @@ func TestCache(t *testing.T) {
|
|||||||
assert.Equal(t, "author1", vv[1])
|
assert.Equal(t, "author1", vv[1])
|
||||||
|
|
||||||
assert.NotNil(t, err)
|
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) {
|
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
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math"
|
"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) {
|
func incr(originVal interface{}) (interface{}, error) {
|
||||||
switch val := originVal.(type) {
|
switch val := originVal.(type) {
|
||||||
case int:
|
case int:
|
||||||
tmp := val + 1
|
tmp := val + 1
|
||||||
if val > 0 && tmp < 0 {
|
if val > 0 && tmp < 0 {
|
||||||
return nil, fmt.Errorf("increment would overflow")
|
return nil, ErrIncrementOverflow
|
||||||
}
|
}
|
||||||
return tmp, nil
|
return tmp, nil
|
||||||
case int32:
|
case int32:
|
||||||
if val == math.MaxInt32 {
|
if val == math.MaxInt32 {
|
||||||
return nil, fmt.Errorf("increment would overflow")
|
return nil, ErrIncrementOverflow
|
||||||
}
|
}
|
||||||
return val + 1, nil
|
return val + 1, nil
|
||||||
case int64:
|
case int64:
|
||||||
if val == math.MaxInt64 {
|
if val == math.MaxInt64 {
|
||||||
return nil, fmt.Errorf("increment would overflow")
|
return nil, ErrIncrementOverflow
|
||||||
}
|
}
|
||||||
return val + 1, nil
|
return val + 1, nil
|
||||||
case uint:
|
case uint:
|
||||||
tmp := val + 1
|
tmp := val + 1
|
||||||
if tmp < val {
|
if tmp < val {
|
||||||
return nil, fmt.Errorf("increment would overflow")
|
return nil, ErrIncrementOverflow
|
||||||
}
|
}
|
||||||
return tmp, nil
|
return tmp, nil
|
||||||
case uint32:
|
case uint32:
|
||||||
if val == math.MaxUint32 {
|
if val == math.MaxUint32 {
|
||||||
return nil, fmt.Errorf("increment would overflow")
|
return nil, ErrIncrementOverflow
|
||||||
}
|
}
|
||||||
return val + 1, nil
|
return val + 1, nil
|
||||||
case uint64:
|
case uint64:
|
||||||
if val == math.MaxUint64 {
|
if val == math.MaxUint64 {
|
||||||
return nil, fmt.Errorf("increment would overflow")
|
return nil, ErrIncrementOverflow
|
||||||
}
|
}
|
||||||
return val + 1, nil
|
return val + 1, nil
|
||||||
default:
|
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:
|
case int:
|
||||||
tmp := val - 1
|
tmp := val - 1
|
||||||
if val < 0 && tmp > 0 {
|
if val < 0 && tmp > 0 {
|
||||||
return nil, fmt.Errorf("decrement would overflow")
|
return nil, ErrDecrementOverflow
|
||||||
}
|
}
|
||||||
return tmp, nil
|
return tmp, nil
|
||||||
case int32:
|
case int32:
|
||||||
if val == math.MinInt32 {
|
if val == math.MinInt32 {
|
||||||
return nil, fmt.Errorf("decrement would overflow")
|
return nil, ErrDecrementOverflow
|
||||||
}
|
}
|
||||||
return val - 1, nil
|
return val - 1, nil
|
||||||
case int64:
|
case int64:
|
||||||
if val == math.MinInt64 {
|
if val == math.MinInt64 {
|
||||||
return nil, fmt.Errorf("decrement would overflow")
|
return nil, ErrDecrementOverflow
|
||||||
}
|
}
|
||||||
return val - 1, nil
|
return val - 1, nil
|
||||||
case uint:
|
case uint:
|
||||||
if val == 0 {
|
if val == 0 {
|
||||||
return nil, fmt.Errorf("decrement would overflow")
|
return nil, ErrDecrementOverflow
|
||||||
}
|
}
|
||||||
return val - 1, nil
|
return val - 1, nil
|
||||||
case uint32:
|
case uint32:
|
||||||
if val == 0 {
|
if val == 0 {
|
||||||
return nil, fmt.Errorf("increment would overflow")
|
return nil, ErrDecrementOverflow
|
||||||
}
|
}
|
||||||
return val - 1, nil
|
return val - 1, nil
|
||||||
case uint64:
|
case uint64:
|
||||||
if val == 0 {
|
if val == 0 {
|
||||||
return nil, fmt.Errorf("increment would overflow")
|
return nil, ErrDecrementOverflow
|
||||||
}
|
}
|
||||||
return val - 1, nil
|
return val - 1, nil
|
||||||
default:
|
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)
|
var updateVal interface{} = int(2)
|
||||||
val, err := incr(originVal)
|
val, err := incr(originVal)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, updateVal, val)
|
assert.Equal(t, val, updateVal)
|
||||||
|
|
||||||
_, err = incr(int(1<<(strconv.IntSize-1) - 1))
|
_, err = incr(int(1<<(strconv.IntSize-1) - 1))
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrIncrementOverflow, err)
|
||||||
|
|
||||||
// int32
|
// int32
|
||||||
originVal = int32(1)
|
originVal = int32(1)
|
||||||
updateVal = int32(2)
|
updateVal = int32(2)
|
||||||
val, err = incr(originVal)
|
val, err = incr(originVal)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, updateVal, val)
|
assert.Equal(t, val, updateVal)
|
||||||
|
|
||||||
_, err = incr(int32(math.MaxInt32))
|
_, err = incr(int32(math.MaxInt32))
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrIncrementOverflow, err)
|
||||||
|
|
||||||
// int64
|
// int64
|
||||||
originVal = int64(1)
|
originVal = int64(1)
|
||||||
updateVal = int64(2)
|
updateVal = int64(2)
|
||||||
val, err = incr(originVal)
|
val, err = incr(originVal)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, val, updateVal)
|
||||||
assert.Equal(t, updateVal, val)
|
|
||||||
|
|
||||||
_, err = incr(int64(math.MaxInt64))
|
_, err = incr(int64(math.MaxInt64))
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrIncrementOverflow, err)
|
||||||
|
|
||||||
// uint
|
// uint
|
||||||
originVal = uint(1)
|
originVal = uint(1)
|
||||||
updateVal = uint(2)
|
updateVal = uint(2)
|
||||||
val, err = incr(originVal)
|
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))
|
_, err = incr(uint(1<<(strconv.IntSize) - 1))
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrIncrementOverflow, err)
|
||||||
|
|
||||||
// uint32
|
// uint32
|
||||||
originVal = uint32(1)
|
originVal = uint32(1)
|
||||||
updateVal = uint32(2)
|
updateVal = uint32(2)
|
||||||
val, err = incr(originVal)
|
val, err = incr(originVal)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, updateVal, val)
|
assert.Equal(t, val, updateVal)
|
||||||
|
|
||||||
_, err = incr(uint32(math.MaxUint32))
|
_, err = incr(uint32(math.MaxUint32))
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrIncrementOverflow, err)
|
||||||
|
|
||||||
// uint64
|
// uint64
|
||||||
originVal = uint64(1)
|
originVal = uint64(1)
|
||||||
updateVal = uint64(2)
|
updateVal = uint64(2)
|
||||||
val, err = incr(originVal)
|
val, err = incr(originVal)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, updateVal, val)
|
assert.Equal(t, val, updateVal)
|
||||||
_, err = incr(uint64(math.MaxUint64))
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
|
|
||||||
|
_, err = incr(uint64(math.MaxUint64))
|
||||||
|
assert.Equal(t, ErrIncrementOverflow, err)
|
||||||
// other type
|
// other type
|
||||||
_, err = incr("string")
|
_, err = incr("string")
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrNotIntegerType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecr(t *testing.T) {
|
func TestDecr(t *testing.T) {
|
||||||
@ -77,57 +80,61 @@ func TestDecr(t *testing.T) {
|
|||||||
var updateVal interface{} = int(1)
|
var updateVal interface{} = int(1)
|
||||||
val, err := decr(originVal)
|
val, err := decr(originVal)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, updateVal, val)
|
assert.Equal(t, val, updateVal)
|
||||||
|
|
||||||
_, err = decr(int(-1 << (strconv.IntSize - 1)))
|
_, err = decr(int(-1 << (strconv.IntSize - 1)))
|
||||||
|
assert.Equal(t, ErrDecrementOverflow, err)
|
||||||
assert.NotNil(t, err)
|
|
||||||
|
|
||||||
// int32
|
// int32
|
||||||
originVal = int32(2)
|
originVal = int32(2)
|
||||||
updateVal = int32(1)
|
updateVal = int32(1)
|
||||||
val, err = decr(originVal)
|
val, err = decr(originVal)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, updateVal, val)
|
assert.Equal(t, val, updateVal)
|
||||||
|
|
||||||
_, err = decr(int32(math.MinInt32))
|
_, err = decr(int32(math.MinInt32))
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrDecrementOverflow, err)
|
||||||
|
|
||||||
// int64
|
// int64
|
||||||
originVal = int64(2)
|
originVal = int64(2)
|
||||||
updateVal = int64(1)
|
updateVal = int64(1)
|
||||||
val, err = decr(originVal)
|
val, err = decr(originVal)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, updateVal, val)
|
assert.Equal(t, val, updateVal)
|
||||||
|
|
||||||
_, err = decr(int64(math.MinInt64))
|
_, err = decr(int64(math.MinInt64))
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrDecrementOverflow, err)
|
||||||
|
|
||||||
// uint
|
// uint
|
||||||
originVal = uint(2)
|
originVal = uint(2)
|
||||||
updateVal = uint(1)
|
updateVal = uint(1)
|
||||||
val, err = decr(originVal)
|
val, err = decr(originVal)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, updateVal, val)
|
assert.Equal(t, val, updateVal)
|
||||||
|
|
||||||
_, err = decr(uint(0))
|
_, err = decr(uint(0))
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrDecrementOverflow, err)
|
||||||
|
|
||||||
// uint32
|
// uint32
|
||||||
originVal = uint32(2)
|
originVal = uint32(2)
|
||||||
updateVal = uint32(1)
|
updateVal = uint32(1)
|
||||||
val, err = decr(originVal)
|
val, err = decr(originVal)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, updateVal, val)
|
assert.Equal(t, val, updateVal)
|
||||||
|
|
||||||
_, err = decr(uint32(0))
|
_, err = decr(uint32(0))
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrDecrementOverflow, err)
|
||||||
|
|
||||||
// uint64
|
// uint64
|
||||||
originVal = uint64(2)
|
originVal = uint64(2)
|
||||||
updateVal = uint64(1)
|
updateVal = uint64(1)
|
||||||
val, err = decr(originVal)
|
val, err = decr(originVal)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, updateVal, val)
|
assert.Equal(t, val, updateVal)
|
||||||
|
|
||||||
_, err = decr(uint64(0))
|
_, err = decr(uint64(0))
|
||||||
assert.NotNil(t, err)
|
assert.Equal(t, ErrDecrementOverflow, err)
|
||||||
|
|
||||||
// other type
|
// other type
|
||||||
_, err = decr("string")
|
_, 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"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/beego/beego/v2/core/berror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileCacheItem is basic unit of file cache adapter which
|
// FileCacheItem is basic unit of file cache adapter which
|
||||||
@ -73,38 +73,60 @@ func (fc *FileCache) StartAndGC(config string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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()
|
const cpKey = "CachePath"
|
||||||
return nil
|
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
|
// Init makes new a dir for file cache if it does not already exist
|
||||||
func (fc *FileCache) Init() {
|
func (fc *FileCache) Init() error {
|
||||||
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
|
ok, err := exists(fc.CachePath)
|
||||||
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
|
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.
|
// 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()
|
m := md5.New()
|
||||||
io.WriteString(m, key)
|
_, _ = io.WriteString(m, key)
|
||||||
keyMd5 := hex.EncodeToString(m.Sum(nil))
|
keyMd5 := hex.EncodeToString(m.Sum(nil))
|
||||||
cachePath := fc.CachePath
|
cachePath := fc.CachePath
|
||||||
switch fc.DirectoryLevel {
|
switch fc.DirectoryLevel {
|
||||||
@ -113,18 +135,29 @@ func (fc *FileCache) getCacheFileName(key string) string {
|
|||||||
case 1:
|
case 1:
|
||||||
cachePath = filepath.Join(cachePath, keyMd5[0:2])
|
cachePath = filepath.Join(cachePath, keyMd5[0:2])
|
||||||
}
|
}
|
||||||
|
ok, err := exists(cachePath)
|
||||||
if ok, _ := exists(cachePath); !ok { // todo : error handle
|
if err != nil {
|
||||||
_ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle
|
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.
|
// Get value from file cache.
|
||||||
// if nonexistent or expired return an empty string.
|
// if nonexistent or expired return an empty string.
|
||||||
func (fc *FileCache) Get(ctx context.Context, key string) (interface{}, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -136,7 +169,7 @@ func (fc *FileCache) Get(ctx context.Context, key string) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if to.Expired.Before(time.Now()) {
|
if to.Expired.Before(time.Now()) {
|
||||||
return nil, errors.New("The key is expired")
|
return nil, ErrKeyExpired
|
||||||
}
|
}
|
||||||
return to.Data, nil
|
return to.Data, nil
|
||||||
}
|
}
|
||||||
@ -159,7 +192,7 @@ func (fc *FileCache) GetMulti(ctx context.Context, keys []string) ([]interface{}
|
|||||||
if len(keysErr) == 0 {
|
if len(keysErr) == 0 {
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
return rc, errors.New(strings.Join(keysErr, "; "))
|
return rc, berror.Error(MultiGetFailed, strings.Join(keysErr, "; "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put value into file cache.
|
// Put value into file cache.
|
||||||
@ -179,14 +212,26 @@ func (fc *FileCache) Put(ctx context.Context, key string, val interface{}, timeo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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.
|
// Delete file cache value.
|
||||||
func (fc *FileCache) Delete(ctx context.Context, key string) error {
|
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 {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
@ -224,8 +269,11 @@ func (fc *FileCache) Decr(ctx context.Context, key string) error {
|
|||||||
|
|
||||||
// IsExist checks if value exists.
|
// IsExist checks if value exists.
|
||||||
func (fc *FileCache) IsExist(ctx context.Context, key string) (bool, error) {
|
func (fc *FileCache) IsExist(ctx context.Context, key string) (bool, error) {
|
||||||
ret, _ := exists(fc.getCacheFileName(key))
|
fn, err := fc.getCacheFileName(key)
|
||||||
return ret, nil
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return exists(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearAll cleans cached files (not implemented)
|
// ClearAll cleans cached files (not implemented)
|
||||||
@ -242,13 +290,19 @@ func exists(path string) (bool, error) {
|
|||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return false, nil
|
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.
|
// FileGetContents Reads bytes from a file.
|
||||||
// if non-existent, create this file.
|
// if non-existent, create this file.
|
||||||
func FileGetContents(filename string) (data []byte, e error) {
|
func FileGetContents(filename string) ([]byte, error) {
|
||||||
return ioutil.ReadFile(filename)
|
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.
|
// FilePutContents puts bytes into a file.
|
||||||
@ -263,16 +317,21 @@ func GobEncode(data interface{}) ([]byte, error) {
|
|||||||
enc := gob.NewEncoder(buf)
|
enc := gob.NewEncoder(buf)
|
||||||
err := enc.Encode(data)
|
err := enc.Encode(data)
|
||||||
if err != nil {
|
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.
|
// GobDecode Gob decodes a file cache item.
|
||||||
func GobDecode(data []byte, to *FileCacheItem) error {
|
func GobDecode(data []byte, to *FileCacheItem) error {
|
||||||
buf := bytes.NewBuffer(data)
|
buf := bytes.NewBuffer(data)
|
||||||
dec := gob.NewDecoder(buf)
|
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() {
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -40,6 +39,7 @@ import (
|
|||||||
"github.com/bradfitz/gomemcache/memcache"
|
"github.com/bradfitz/gomemcache/memcache"
|
||||||
|
|
||||||
"github.com/beego/beego/v2/client/cache"
|
"github.com/beego/beego/v2/client/cache"
|
||||||
|
"github.com/beego/beego/v2/core/berror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cache Memcache adapter.
|
// Cache Memcache adapter.
|
||||||
@ -55,30 +55,25 @@ func NewMemCache() cache.Cache {
|
|||||||
|
|
||||||
// Get get value from memcache.
|
// Get get value from memcache.
|
||||||
func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
|
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 {
|
if item, err := rc.conn.Get(key); err == nil {
|
||||||
return item.Value, nil
|
return item.Value, nil
|
||||||
} else {
|
} 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.
|
// GetMulti gets a value from a key in memcache.
|
||||||
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
||||||
rv := make([]interface{}, len(keys))
|
rv := make([]interface{}, len(keys))
|
||||||
if rc.conn == nil {
|
|
||||||
if err := rc.connectInit(); err != nil {
|
|
||||||
return rv, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mv, err := rc.conn.GetMulti(keys)
|
mv, err := rc.conn.GetMulti(keys)
|
||||||
if err != nil {
|
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)
|
keysErr := make([]string, 0)
|
||||||
@ -93,78 +88,54 @@ func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, er
|
|||||||
if len(keysErr) == 0 {
|
if len(keysErr) == 0 {
|
||||||
return rv, nil
|
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.
|
// Put puts a value into memcache.
|
||||||
func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error {
|
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)}
|
item := memcache.Item{Key: key, Expiration: int32(timeout / time.Second)}
|
||||||
if v, ok := val.([]byte); ok {
|
if v, ok := val.([]byte); ok {
|
||||||
item.Value = v
|
item.Value = v
|
||||||
} else if str, ok := val.(string); ok {
|
} else if str, ok := val.(string); ok {
|
||||||
item.Value = []byte(str)
|
item.Value = []byte(str)
|
||||||
} else {
|
} 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.
|
// Delete deletes a value in memcache.
|
||||||
func (rc *Cache) Delete(ctx context.Context, key string) error {
|
func (rc *Cache) Delete(ctx context.Context, key string) error {
|
||||||
if rc.conn == nil {
|
return berror.Wrapf(rc.conn.Delete(key), cache.MemCacheCurdFailed,
|
||||||
if err := rc.connectInit(); err != nil {
|
"could not delete key-value from memcache, key: %s", key)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rc.conn.Delete(key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Incr increases counter.
|
// Incr increases counter.
|
||||||
func (rc *Cache) Incr(ctx context.Context, key string) error {
|
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)
|
_, 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.
|
// Decr decreases counter.
|
||||||
func (rc *Cache) Decr(ctx context.Context, key string) error {
|
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)
|
_, 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.
|
// IsExist checks if a value exists in memcache.
|
||||||
func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) {
|
func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) {
|
||||||
if rc.conn == nil {
|
_, err := rc.Get(ctx, key)
|
||||||
if err := rc.connectInit(); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err := rc.conn.Get(key)
|
|
||||||
return err == nil, err
|
return err == nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearAll clears all cache in memcache.
|
// ClearAll clears all cache in memcache.
|
||||||
func (rc *Cache) ClearAll(context.Context) error {
|
func (rc *Cache) ClearAll(context.Context) error {
|
||||||
if rc.conn == nil {
|
return berror.Wrap(rc.conn.FlushAll(), cache.MemCacheCurdFailed,
|
||||||
if err := rc.connectInit(); err != nil {
|
"try to clear all key-value pairs failed")
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rc.conn.FlushAll()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartAndGC starts the memcache adapter.
|
// 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
|
// If an error occurs during connecting, an error is returned
|
||||||
func (rc *Cache) StartAndGC(config string) error {
|
func (rc *Cache) StartAndGC(config string) error {
|
||||||
var cf map[string]string
|
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 {
|
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"], ";")
|
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...)
|
rc.conn = memcache.New(rc.conninfo...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
3
client/cache/memcache/memcache_test.go
vendored
3
client/cache/memcache/memcache_test.go
vendored
@ -19,6 +19,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ func TestMemcacheCache(t *testing.T) {
|
|||||||
assert.Equal(t, "author1", string(vv[1].([]byte)))
|
assert.Equal(t, "author1", string(vv[1].([]byte)))
|
||||||
|
|
||||||
assert.NotNil(t, err)
|
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()))
|
assert.Nil(t, bm.ClearAll(context.Background()))
|
||||||
// test clear all
|
// test clear all
|
||||||
|
|||||||
30
client/cache/memory.go
vendored
30
client/cache/memory.go
vendored
@ -17,11 +17,12 @@ package cache
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/beego/beego/v2/core/berror"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -29,8 +30,6 @@ var (
|
|||||||
DefaultEvery = 60 // 1 minute
|
DefaultEvery = 60 // 1 minute
|
||||||
)
|
)
|
||||||
|
|
||||||
const keyNotExistMsg = "key not exist"
|
|
||||||
|
|
||||||
// MemoryItem stores memory cache item.
|
// MemoryItem stores memory cache item.
|
||||||
type MemoryItem struct {
|
type MemoryItem struct {
|
||||||
val interface{}
|
val interface{}
|
||||||
@ -66,13 +65,14 @@ func NewMemoryCache() Cache {
|
|||||||
func (bc *MemoryCache) Get(ctx context.Context, key string) (interface{}, error) {
|
func (bc *MemoryCache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||||
bc.RLock()
|
bc.RLock()
|
||||||
defer bc.RUnlock()
|
defer bc.RUnlock()
|
||||||
if itm, ok := bc.items[key]; ok {
|
if itm, ok :=
|
||||||
|
bc.items[key]; ok {
|
||||||
if itm.isExpire() {
|
if itm.isExpire() {
|
||||||
return nil, errors.New("the key is expired")
|
return nil, ErrKeyExpired
|
||||||
}
|
}
|
||||||
return itm.val, nil
|
return itm.val, nil
|
||||||
}
|
}
|
||||||
return nil, errors.New(keyNotExistMsg)
|
return nil, ErrKeyNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMulti gets caches from memory.
|
// GetMulti gets caches from memory.
|
||||||
@ -93,7 +93,7 @@ func (bc *MemoryCache) GetMulti(ctx context.Context, keys []string) ([]interface
|
|||||||
if len(keysErr) == 0 {
|
if len(keysErr) == 0 {
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
return rc, errors.New(strings.Join(keysErr, "; "))
|
return rc, berror.Error(MultiGetFailed, strings.Join(keysErr, "; "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put puts cache into memory.
|
// 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.
|
// 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 {
|
func (bc *MemoryCache) Delete(ctx context.Context, key string) error {
|
||||||
bc.Lock()
|
bc.Lock()
|
||||||
defer bc.Unlock()
|
defer bc.Unlock()
|
||||||
if _, ok := bc.items[key]; !ok {
|
|
||||||
|
|
||||||
return errors.New(keyNotExistMsg)
|
|
||||||
}
|
|
||||||
delete(bc.items, key)
|
delete(bc.items, key)
|
||||||
if _, ok := bc.items[key]; ok {
|
|
||||||
return errors.New("delete key error")
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +125,7 @@ func (bc *MemoryCache) Incr(ctx context.Context, key string) error {
|
|||||||
defer bc.Unlock()
|
defer bc.Unlock()
|
||||||
itm, ok := bc.items[key]
|
itm, ok := bc.items[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New(keyNotExistMsg)
|
return ErrKeyNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
val, err := incr(itm.val)
|
val, err := incr(itm.val)
|
||||||
@ -148,7 +142,7 @@ func (bc *MemoryCache) Decr(ctx context.Context, key string) error {
|
|||||||
defer bc.Unlock()
|
defer bc.Unlock()
|
||||||
itm, ok := bc.items[key]
|
itm, ok := bc.items[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New(keyNotExistMsg)
|
return ErrKeyNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
val, err := decr(itm.val)
|
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.
|
// StartAndGC starts memory cache. Checks expiration in every clock time.
|
||||||
func (bc *MemoryCache) StartAndGC(config string) error {
|
func (bc *MemoryCache) StartAndGC(config string) error {
|
||||||
var cf map[string]int
|
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 {
|
if _, ok := cf["interval"]; !ok {
|
||||||
cf = make(map[string]int)
|
cf = make(map[string]int)
|
||||||
cf["interval"] = DefaultEvery
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -41,6 +40,7 @@ import (
|
|||||||
"github.com/gomodule/redigo/redis"
|
"github.com/gomodule/redigo/redis"
|
||||||
|
|
||||||
"github.com/beego/beego/v2/client/cache"
|
"github.com/beego/beego/v2/client/cache"
|
||||||
|
"github.com/beego/beego/v2/core/berror"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -67,15 +67,20 @@ func NewRedisCache() cache.Cache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute the redis commands. args[0] must be the key name
|
// Execute the redis commands. args[0] must be the key name
|
||||||
func (rc *Cache) do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
func (rc *Cache) do(commandName string, args ...interface{}) (interface{}, error) {
|
||||||
if len(args) < 1 {
|
|
||||||
return nil, errors.New("missing required arguments")
|
|
||||||
}
|
|
||||||
args[0] = rc.associate(args[0])
|
args[0] = rc.associate(args[0])
|
||||||
c := rc.p.Get()
|
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.
|
// associate with config key.
|
||||||
@ -95,7 +100,9 @@ func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
|
|||||||
// GetMulti gets cache from redis.
|
// GetMulti gets cache from redis.
|
||||||
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
||||||
c := rc.p.Get()
|
c := rc.p.Get()
|
||||||
defer c.Close()
|
defer func() {
|
||||||
|
_ = c.Close()
|
||||||
|
}()
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
args = append(args, rc.associate(key))
|
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
|
// 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 {
|
func (rc *Cache) ClearAll(context.Context) error {
|
||||||
cachedKeys, err := rc.Scan(rc.key + ":*")
|
cachedKeys, err := rc.Scan(rc.key + ":*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c := rc.p.Get()
|
c := rc.p.Get()
|
||||||
defer c.Close()
|
defer func() {
|
||||||
|
_ = c.Close()
|
||||||
|
}()
|
||||||
for _, str := range cachedKeys {
|
for _, str := range cachedKeys {
|
||||||
if _, err = c.Do("DEL", str); err != nil {
|
if _, err = c.Do("DEL", str); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -155,7 +165,9 @@ func (rc *Cache) ClearAll(context.Context) error {
|
|||||||
// Scan scans all keys matching a given pattern.
|
// Scan scans all keys matching a given pattern.
|
||||||
func (rc *Cache) Scan(pattern string) (keys []string, err error) {
|
func (rc *Cache) Scan(pattern string) (keys []string, err error) {
|
||||||
c := rc.p.Get()
|
c := rc.p.Get()
|
||||||
defer c.Close()
|
defer func() {
|
||||||
|
_ = c.Close()
|
||||||
|
}()
|
||||||
var (
|
var (
|
||||||
cursor uint64 = 0 // start
|
cursor uint64 = 0 // start
|
||||||
result []interface{}
|
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
|
// Cached items in redis are stored forever, no garbage collection happens
|
||||||
func (rc *Cache) StartAndGC(config string) error {
|
func (rc *Cache) StartAndGC(config string) error {
|
||||||
var cf map[string]string
|
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 {
|
if _, ok := cf["key"]; !ok {
|
||||||
cf["key"] = DefaultKey
|
cf["key"] = DefaultKey
|
||||||
}
|
}
|
||||||
if _, ok := cf["conn"]; !ok {
|
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>
|
// Format redis://<password>@<host>:<port>
|
||||||
@ -229,9 +244,16 @@ func (rc *Cache) StartAndGC(config string) error {
|
|||||||
rc.connectInit()
|
rc.connectInit()
|
||||||
|
|
||||||
c := rc.p.Get()
|
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.
|
// connect to redis.
|
||||||
@ -239,19 +261,20 @@ func (rc *Cache) connectInit() {
|
|||||||
dialFunc := func() (c redis.Conn, err error) {
|
dialFunc := func() (c redis.Conn, err error) {
|
||||||
c, err = redis.Dial("tcp", rc.conninfo)
|
c, err = redis.Dial("tcp", rc.conninfo)
|
||||||
if err != nil {
|
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 rc.password != "" {
|
||||||
if _, err := c.Do("AUTH", rc.password); err != nil {
|
if _, err = c.Do("AUTH", rc.password); err != nil {
|
||||||
c.Close()
|
_ = c.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, selecterr := c.Do("SELECT", rc.dbNum)
|
_, selecterr := c.Do("SELECT", rc.dbNum)
|
||||||
if selecterr != nil {
|
if selecterr != nil {
|
||||||
c.Close()
|
_ = c.Close()
|
||||||
return nil, selecterr
|
return nil, selecterr
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
109
client/cache/ssdb/ssdb.go
vendored
109
client/cache/ssdb/ssdb.go
vendored
@ -3,7 +3,6 @@ package ssdb
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -12,6 +11,7 @@ import (
|
|||||||
"github.com/ssdb/gossdb/ssdb"
|
"github.com/ssdb/gossdb/ssdb"
|
||||||
|
|
||||||
"github.com/beego/beego/v2/client/cache"
|
"github.com/beego/beego/v2/client/cache"
|
||||||
|
"github.com/beego/beego/v2/core/berror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cache SSDB adapter
|
// Cache SSDB adapter
|
||||||
@ -27,31 +27,21 @@ func NewSsdbCache() cache.Cache {
|
|||||||
|
|
||||||
// Get gets a key's value from memcache.
|
// Get gets a key's value from memcache.
|
||||||
func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
|
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)
|
value, err := rc.conn.Get(key)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return value, 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.
|
// GetMulti gets one or keys values from ssdb.
|
||||||
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
||||||
size := len(keys)
|
size := len(keys)
|
||||||
values := make([]interface{}, size)
|
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)
|
res, err := rc.conn.Do("multi_get", keys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return values, err
|
return values, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "multi_get failed, key: %v", keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
resSize := len(res)
|
resSize := len(res)
|
||||||
@ -70,7 +60,7 @@ func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(keysErr) != 0 {
|
if len(keysErr) != 0 {
|
||||||
return values, fmt.Errorf(strings.Join(keysErr, "; "))
|
return values, berror.Error(cache.MultiGetFailed, strings.Join(keysErr, "; "))
|
||||||
}
|
}
|
||||||
|
|
||||||
return values, nil
|
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
|
// DelMulti deletes one or more keys from memcache
|
||||||
func (rc *Cache) DelMulti(keys []string) error {
|
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)
|
_, 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.
|
// Put puts value into memcache.
|
||||||
// value: must be of type string
|
// value: must be of type string
|
||||||
func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error {
|
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)
|
v, ok := val.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("value must string")
|
return berror.Errorf(cache.InvalidSsdbCacheValue, "value must be string: %v", val)
|
||||||
}
|
}
|
||||||
var resp []string
|
var resp []string
|
||||||
var err error
|
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)
|
resp, err = rc.conn.Do("setx", key, v, ttl)
|
||||||
}
|
}
|
||||||
if err != nil {
|
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" {
|
if len(resp) == 2 && resp[0] == "ok" {
|
||||||
return nil
|
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.
|
// Delete deletes a value in memcache.
|
||||||
func (rc *Cache) Delete(ctx context.Context, key string) error {
|
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)
|
_, err := rc.conn.Del(key)
|
||||||
return err
|
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "del failed: %s", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Incr increases a key's counter.
|
// Incr increases a key's counter.
|
||||||
func (rc *Cache) Incr(ctx context.Context, key string) error {
|
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)
|
_, 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.
|
// Decr decrements a key's counter.
|
||||||
func (rc *Cache) Decr(ctx context.Context, key string) error {
|
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)
|
_, 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.
|
// IsExist checks if a key exists in memcache.
|
||||||
func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) {
|
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)
|
resp, err := rc.conn.Do("exists", key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "exists failed: %s", key)
|
||||||
}
|
}
|
||||||
if len(resp) == 2 && resp[1] == "1" {
|
if len(resp) == 2 && resp[1] == "1" {
|
||||||
return true, nil
|
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 {
|
func (rc *Cache) ClearAll(context.Context) error {
|
||||||
if rc.conn == nil {
|
|
||||||
if err := rc.connectInit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyStart, keyEnd, limit := "", "", 50
|
keyStart, keyEnd, limit := "", "", 50
|
||||||
resp, err := rc.Scan(keyStart, keyEnd, limit)
|
resp, err := rc.Scan(keyStart, keyEnd, limit)
|
||||||
for err == nil {
|
for err == nil {
|
||||||
@ -187,21 +143,16 @@ func (rc *Cache) ClearAll(context.Context) error {
|
|||||||
}
|
}
|
||||||
_, e := rc.conn.Do("multi_del", keys)
|
_, e := rc.conn.Do("multi_del", keys)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e
|
return berror.Wrapf(e, cache.SsdbCacheCurdFailed, "multi_del failed: %v", keys)
|
||||||
}
|
}
|
||||||
keyStart = resp[size-2]
|
keyStart = resp[size-2]
|
||||||
resp, err = rc.Scan(keyStart, keyEnd, limit)
|
resp, err = rc.Scan(keyStart, keyEnd, limit)
|
||||||
}
|
}
|
||||||
return err
|
return berror.Wrap(err, cache.SsdbCacheCurdFailed, "scan failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan key all cached in ssdb.
|
// Scan key all cached in ssdb.
|
||||||
func (rc *Cache) Scan(keyStart string, keyEnd string, limit int) ([]string, error) {
|
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)
|
resp, err := rc.conn.Do("scan", keyStart, keyEnd, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// If an error occurs during connection, an error is returned
|
||||||
func (rc *Cache) StartAndGC(config string) error {
|
func (rc *Cache) StartAndGC(config string) error {
|
||||||
var cf map[string]string
|
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 {
|
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"], ";")
|
rc.conninfo = strings.Split(cf["conn"], ";")
|
||||||
if rc.conn == nil {
|
return rc.connectInit()
|
||||||
if err := rc.connectInit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// connect to memcache and keep the connection.
|
// connect to memcache and keep the connection.
|
||||||
func (rc *Cache) connectInit() error {
|
func (rc *Cache) connectInit() error {
|
||||||
conninfoArray := strings.Split(rc.conninfo[0], ":")
|
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]
|
host := conninfoArray[0]
|
||||||
port, e := strconv.Atoi(conninfoArray[1])
|
port, e := strconv.Atoi(conninfoArray[1])
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e
|
return berror.Errorf(cache.InvalidSsdbCacheCfg, "Port is invalid. It must be integer, %s", rc.conninfo[0])
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
rc.conn, err = ssdb.Connect(host, port)
|
if rc.conn, err = ssdb.Connect(host, port); err != nil {
|
||||||
return err
|
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() {
|
func init() {
|
||||||
|
|||||||
3
client/cache/ssdb/ssdb_test.go
vendored
3
client/cache/ssdb/ssdb_test.go
vendored
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -89,7 +90,7 @@ func TestSsdbcacheCache(t *testing.T) {
|
|||||||
assert.Nil(t, vv[1])
|
assert.Nil(t, vv[1])
|
||||||
|
|
||||||
assert.NotNil(t, err)
|
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
|
// test clear all done
|
||||||
assert.Nil(t, ssdb.ClearAll(context.Background()))
|
assert.Nil(t, ssdb.ClearAll(context.Background()))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user