Skip to content

Commit

Permalink
x
Browse files Browse the repository at this point in the history
  • Loading branch information
fioman committed Nov 16, 2021
1 parent b1506ab commit 7e3e8ae
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 34 deletions.
8 changes: 7 additions & 1 deletion cache/cache.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package cache

import "errors"

type Cache interface {
Init(opts ...Option) error
Options() Options
Get(key string, resultPtr interface{}, opts ...ReadOption) error
BatchGet(keys []string, resultsPtr interface{}, opts ...ReadOption) error
Set(key string, value interface{}, opts ...WriteOption) error
Delete(key string, opts ...DeleteOption) error
}
BatchDelete(keys []string, opts ...DeleteOption) error
}

var ErrNil = errors.New("nil returned")
6 changes: 2 additions & 4 deletions cache/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

type Options struct {
Context context.Context
Prefix string
Prefix string
}

type Option func(o *Options)
Expand All @@ -29,9 +29,7 @@ func WriteExpiry(t time.Duration) WriteOption {
}
}


type DeleteOptions struct {

}

type DeleteOption func(o *DeleteOptions)
type DeleteOption func(o *DeleteOptions)
101 changes: 82 additions & 19 deletions cache/redis/redis.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
package redis

import(
import (
"context"
"encoding/json"
"fmt"
"time"
"strings"
"github.com/xxxmicro/base/cache"
"github.com/garyburd/redigo/redis"
"encoding/json"
"github.com/xxxmicro/base/cache"
"reflect"
"strings"
"time"
)

type RedisCache struct {
options cache.Options
r *redis.Pool
r *redis.Pool
}

func NewCache(opts ...cache.Option) cache.Cache {
options := cache.Options{
Context: context.Background(),
}

for _, o := range opts {
o(&options)
}

return &RedisCache{
options: options,
}
Expand All @@ -33,7 +32,6 @@ func (m *RedisCache) Init(opts ...cache.Option) error {
for _, o := range opts {
o(&m.options)
}

r, err := m.connect()
if err != nil {
return err
Expand Down Expand Up @@ -65,7 +63,7 @@ func (m *RedisCache) connect() (*redis.Pool, error) {

if password != "" {
if _, err := c.Do("AUTH", password); err != nil {
c.Close()
_ = c.Close()
return nil, err
}
}
Expand All @@ -76,12 +74,15 @@ func (m *RedisCache) connect() (*redis.Pool, error) {
return err
},
}

return pool, nil
}

func (m *RedisCache) prefix(key string) string {
return fmt.Sprintf("%s:%s", m.options.Prefix, key)
if len(m.options.Prefix) > 0 {
return fmt.Sprintf("%s:%s", m.options.Prefix, key)
} else {
return key
}
}

func (m *RedisCache) String() string {
Expand All @@ -98,14 +99,58 @@ func (m *RedisCache) Get(key string, resultPtr interface{}, opts ...cache.ReadOp

c := m.r.Get()
defer c.Close()

data, err := redis.Bytes(c.Do("GET", key))
if err != nil {
if err == redis.ErrNil {
return cache.ErrNil
} else if err != nil {
return err
}

return json.Unmarshal(data, resultPtr)
}

func (m *RedisCache) BatchGet(keys []string, resultsPtr interface{}, opts ...cache.ReadOption) error {
readOpts := cache.ReadOptions{}
for _, o := range opts {
o(&readOpts)
}

realKeys := make([]interface{}, len(keys))
for i, key := range keys {
realKeys[i] = m.prefix(key)
}

c := m.r.Get()
defer c.Close()

var replies interface{}
var err error
if replies, err = c.Do("MGET", realKeys...); err != nil {
return err
}

sliceType := reflect.TypeOf(resultsPtr).Elem()
valuePtrType := sliceType.Elem()
valueType := valuePtrType.Elem()
slice := reflect.MakeSlice(sliceType, 0, 0)
for _, v := range replies.([]interface{}) {
var data []byte
if data, err = redis.Bytes(v, nil); err == redis.ErrNil {
slice = reflect.Append(slice, reflect.Zero(valuePtrType))
continue
} else if err != nil {
return err
}
valuePtr := reflect.New(valueType)
if err = json.Unmarshal(data, valuePtr.Interface()); err != nil {
return err
}
slice = reflect.Append(slice, valuePtr)
}
reflect.ValueOf(resultsPtr).Elem().Set(slice)
return nil
}

func (m *RedisCache) Set(key string, value interface{}, opts ...cache.WriteOption) error {
writeOpts := cache.WriteOptions{}
for _, o := range opts {
Expand All @@ -130,10 +175,6 @@ func (m *RedisCache) Set(key string, value interface{}, opts ...cache.WriteOptio
return err
}

func (m *RedisCache) Close() error {
return m.r.Close()
}

func (m *RedisCache) Delete(key string, opts ...cache.DeleteOption) error {
deleteOptions := cache.DeleteOptions{}
for _, o := range opts {
Expand All @@ -147,4 +188,26 @@ func (m *RedisCache) Delete(key string, opts ...cache.DeleteOption) error {

_, err := c.Do("DEL", key)
return err
}
}

func (m *RedisCache) BatchDelete(keys []string, opts ...cache.DeleteOption) error {
deleteOptions := cache.DeleteOptions{}
for _, o := range opts {
o(&deleteOptions)
}

realKeys := make([]interface{}, len(keys))
for i, key := range keys {
realKeys[i] = m.prefix(key)
}

c := m.r.Get()
defer c.Close()

_, err := c.Do("DEL", realKeys...)
return err
}

func (m *RedisCache) Close() error {
return m.r.Close()
}
60 changes: 50 additions & 10 deletions cache/redis/redis_cache_test.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package redis_test

import(
"testing"
"time"
"log"
import (
"github.com/stretchr/testify/assert"
"github.com/xxxmicro/base/cache"
"github.com/xxxmicro/base/cache/redis"
"github.com/stretchr/testify/assert"
"log"
"testing"
"time"
)

type User struct {
Name string `json:"name"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
}

Expand All @@ -19,9 +19,9 @@ func TestBasic(t *testing.T) {
s.Init()

key := "hello"
value := &User{Name:"李小龙", CreatedAt: time.Now()}
value := &User{Name: "李小龙", CreatedAt: time.Now()}

err := s.Set(key, value, cache.WriteExpiry(time.Millisecond * 1000))
err := s.Set(key, value, cache.WriteExpiry(time.Millisecond*1000))
assert.NoError(t, err)

time.Sleep(time.Millisecond * 500)
Expand All @@ -35,5 +35,45 @@ func TestBasic(t *testing.T) {

value2 := &User{}
err = s.Get(key, value2)
assert.Error(t, err, "Expected no records in redis store")
}
assert.Equal(t, cache.ErrNil, err)
}

func TestBatch(t *testing.T) {
s := redis.NewCache(redis.WithAddrs(":6379"))
s.Init()

var err error

key1 := "bruceli"
value1 := &User{Name: "李小龙", CreatedAt: time.Now()}

key2 := "jackychen"
value2 := &User{Name: "成龙", CreatedAt: time.Now()}

err = s.Set(key1, value1)
assert.NoError(t, err)

err = s.Set(key2, value2)
assert.NoError(t, err)

users := make([]*User, 0)
err = s.BatchGet([]string{key1, "fatchow", key2}, &users)
assert.NoError(t, err)
assert.Equal(t, 3, len(users))
assert.Equal(t, value1.Name, users[0].Name)
assert.Nil(t, users[1])
assert.Equal(t, value2.Name, users[2].Name)
t.Log(users[0])
t.Log(users[1])
t.Log(users[2])

err = s.BatchDelete([]string{key1, "fatchow", key2})
assert.NoError(t, err)

gotValue := &User{}
err = s.Get(key1, gotValue)
assert.Equal(t, cache.ErrNil, err)

err = s.Get(key2, gotValue)
assert.Equal(t, cache.ErrNil, err)
}

0 comments on commit 7e3e8ae

Please sign in to comment.