add redis cache
This commit is contained in:
parent
4dc5e64dde
commit
673d22fcff
3
go.mod
3
go.mod
|
@ -11,6 +11,7 @@ require (
|
|||
github.com/google/uuid v1.6.0
|
||||
github.com/mattn/go-runewidth v0.0.16
|
||||
github.com/peterh/liner v1.2.2
|
||||
github.com/redis/go-redis/v9 v9.10.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
go.etcd.io/etcd/api/v3 v3.6.0
|
||||
go.etcd.io/etcd/client/v3 v3.6.0
|
||||
|
@ -24,10 +25,12 @@ require (
|
|||
require (
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -1,7 +1,13 @@
|
|||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
|
@ -14,6 +20,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
|
@ -86,6 +94,8 @@ github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
|
|||
github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs=
|
||||
github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
|
|
|
@ -13,9 +13,11 @@ var (
|
|||
|
||||
type Cache interface {
|
||||
// Get gets a cached value by key.
|
||||
Get(ctx context.Context, key string) (any, error)
|
||||
Load(ctx context.Context, key string, val any) error
|
||||
// Get gets a cached value by key.
|
||||
Exists(ctx context.Context, key string) (bool, error)
|
||||
// Put stores a key-value pair into cache.
|
||||
Put(ctx context.Context, key string, val any, d time.Duration) error
|
||||
Store(ctx context.Context, key string, val any, d time.Duration) error
|
||||
// Delete removes a key from cache.
|
||||
Delete(ctx context.Context, key string) error
|
||||
// String returns the name of the implementation.
|
||||
|
@ -27,13 +29,13 @@ func Default() Cache {
|
|||
}
|
||||
|
||||
// Get gets a cached value by key.
|
||||
func Get(ctx context.Context, key string) (any, error) {
|
||||
return std.Get(ctx, key)
|
||||
func Load(ctx context.Context, key string, val any) error {
|
||||
return std.Load(ctx, key, val)
|
||||
}
|
||||
|
||||
// Put stores a key-value pair into cache.
|
||||
func Put(ctx context.Context, key string, val any, d time.Duration) error {
|
||||
return std.Put(ctx, key, val, d)
|
||||
func Store(ctx context.Context, key string, val any, d time.Duration) error {
|
||||
return std.Store(ctx, key, val, d)
|
||||
}
|
||||
|
||||
// String returns the name of the implementation.
|
||||
|
|
|
@ -2,10 +2,18 @@ package memory
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.nobla.cn/golang/aeus/pkg/errors"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrWongType = errors.New("val must be a pointer")
|
||||
ErrNotExists = errors.New("not exists")
|
||||
ErrAaddressable = errors.New("cannot set value: val is not addressable")
|
||||
)
|
||||
|
||||
type memCache struct {
|
||||
|
@ -15,22 +23,47 @@ type memCache struct {
|
|||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *memCache) Get(ctx context.Context, key string) (any, error) {
|
||||
func (c *memCache) Load(ctx context.Context, key string, val any) error {
|
||||
c.RWMutex.RLock()
|
||||
defer c.RWMutex.RUnlock()
|
||||
|
||||
item, found := c.items[key]
|
||||
if !found {
|
||||
return nil, errors.ErrNotFound
|
||||
return ErrNotExists
|
||||
}
|
||||
if item.Expired() {
|
||||
return nil, errors.ErrExpired
|
||||
return ErrNotExists
|
||||
}
|
||||
|
||||
return item.Value, nil
|
||||
refValue := reflect.ValueOf(val)
|
||||
if refValue.Type().Kind() != reflect.Ptr {
|
||||
return ErrWongType
|
||||
}
|
||||
refElem := refValue.Elem()
|
||||
if !refElem.CanSet() {
|
||||
return ErrAaddressable
|
||||
}
|
||||
targetValue := reflect.Indirect(reflect.ValueOf(item.Value))
|
||||
if targetValue.Type() != refElem.Type() {
|
||||
return fmt.Errorf("type mismatch: expected %v, got %v", refElem.Type(), targetValue.Type())
|
||||
}
|
||||
refElem.Set(targetValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *memCache) Put(ctx context.Context, key string, val any, d time.Duration) error {
|
||||
func (c *memCache) Exists(ctx context.Context, key string) (bool, error) {
|
||||
c.RWMutex.RLock()
|
||||
defer c.RWMutex.RUnlock()
|
||||
item, found := c.items[key]
|
||||
if !found {
|
||||
return false, nil
|
||||
}
|
||||
if item.Expired() {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *memCache) Store(ctx context.Context, key string, val any, d time.Duration) error {
|
||||
var e int64
|
||||
if d == DefaultExpiration {
|
||||
d = c.opts.Expiration
|
||||
|
@ -56,9 +89,8 @@ func (c *memCache) Delete(ctx context.Context, key string) error {
|
|||
|
||||
_, found := c.items[key]
|
||||
if !found {
|
||||
return errors.ErrNotFound
|
||||
return ErrNotExists
|
||||
}
|
||||
|
||||
delete(c.items, key)
|
||||
return nil
|
||||
}
|
||||
|
@ -66,3 +98,18 @@ func (c *memCache) Delete(ctx context.Context, key string) error {
|
|||
func (m *memCache) String() string {
|
||||
return "memory"
|
||||
}
|
||||
|
||||
// NewCache returns a new cache.
|
||||
func NewCache(opts ...Option) *memCache {
|
||||
options := NewOptions(opts...)
|
||||
items := make(map[string]Item)
|
||||
|
||||
if len(options.Items) > 0 {
|
||||
items = options.Items
|
||||
}
|
||||
|
||||
return &memCache{
|
||||
opts: options,
|
||||
items: items,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,3 @@ func (i *Item) Expired() bool {
|
|||
|
||||
return time.Now().UnixNano() > i.Expiration
|
||||
}
|
||||
|
||||
// NewCache returns a new cache.
|
||||
func NewCache(opts ...Option) *memCache {
|
||||
options := NewOptions(opts...)
|
||||
items := make(map[string]Item)
|
||||
|
||||
if len(options.Items) > 0 {
|
||||
items = options.Items
|
||||
}
|
||||
|
||||
return &memCache{
|
||||
opts: options,
|
||||
items: items,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type redisCache struct {
|
||||
opts *options
|
||||
}
|
||||
|
||||
func (c *redisCache) buildKey(key string) string {
|
||||
return c.opts.prefix + key
|
||||
}
|
||||
|
||||
func (c *redisCache) Load(ctx context.Context, key string, val any) error {
|
||||
var (
|
||||
err error
|
||||
buf []byte
|
||||
)
|
||||
if buf, err = c.opts.client.Get(ctx, c.buildKey(key)).Bytes(); err == nil {
|
||||
err = json.Unmarshal(buf, val)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *redisCache) Exists(ctx context.Context, key string) (ok bool, err error) {
|
||||
var n int64
|
||||
n, err = c.opts.client.Exists(ctx, c.buildKey(key)).Result()
|
||||
if n > 0 {
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Put stores a key-value pair into cache.
|
||||
func (c *redisCache) Store(ctx context.Context, key string, val any, d time.Duration) error {
|
||||
var (
|
||||
err error
|
||||
buf []byte
|
||||
)
|
||||
if buf, err = json.Marshal(val); err == nil {
|
||||
err = c.opts.client.Set(ctx, c.buildKey(key), buf, d).Err()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete removes a key from cache.
|
||||
func (c *redisCache) Delete(ctx context.Context, key string) error {
|
||||
return c.opts.client.Del(ctx, c.buildKey(key)).Err()
|
||||
}
|
||||
|
||||
// String returns the name of the implementation.
|
||||
func (c *redisCache) String() string {
|
||||
return "redis"
|
||||
}
|
||||
|
||||
func NewCache(opts ...Option) *redisCache {
|
||||
return &redisCache{
|
||||
opts: newOptions(opts...),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package redis
|
||||
|
||||
import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type (
|
||||
options struct {
|
||||
client *redis.Client
|
||||
prefix string
|
||||
}
|
||||
|
||||
Option func(*options)
|
||||
)
|
||||
|
||||
func WithClient(client *redis.Client) Option {
|
||||
return func(o *options) {
|
||||
o.client = client
|
||||
}
|
||||
}
|
||||
|
||||
func WithPrefix(prefix string) Option {
|
||||
return func(o *options) {
|
||||
o.prefix = prefix
|
||||
}
|
||||
}
|
||||
|
||||
// NewOptions returns a new options struct.
|
||||
func newOptions(opts ...Option) *options {
|
||||
options := &options{
|
||||
prefix: "cache:",
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(options)
|
||||
}
|
||||
return options
|
||||
}
|
Loading…
Reference in New Issue