Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Rueidis store #184

Merged
merged 3 commits into from
Dec 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Here is what it brings in detail:
* [Memory (go-cache)](https://github.com/patrickmn/go-cache) (patrickmn/go-cache)
* [Memcache](https://github.com/bradfitz/gomemcache) (bradfitz/memcache)
* [Redis](https://github.com/go-redis/redis) (go-redis/redis)
* [Redis (rueidis)](https://github.com/rueian/rueidis) (rueian/rueidis)
* [Freecache](https://github.com/coocood/freecache) (coocood/freecache)
* [Pegasus](https://pegasus.apache.org/) ([apache/incubator-pegasus](https://github.com/apache/incubator-pegasus)) [benchmark](https://pegasus.apache.org/overview/benchmark/)
* More to come soon
Expand All @@ -55,6 +56,7 @@ go get github.com/eko/gocache/v4/store/memcache
go get github.com/eko/gocache/v4/store/pegasus
go get github.com/eko/gocache/v4/store/redis
go get github.com/eko/gocache/v4/store/rediscluster
go get github.com/eko/gocache/v4/store/rueidis
go get github.com/eko/gocache/v4/store/ristretto
```

Expand Down
15 changes: 12 additions & 3 deletions lib/store/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import (
type Option func(o *Options)

type Options struct {
Cost int64
Expiration time.Duration
Tags []string
Cost int64
Expiration time.Duration
Tags []string
ClientSideCacheExpiration time.Duration
}

func (o *Options) IsEmpty() bool {
Expand Down Expand Up @@ -59,3 +60,11 @@ func WithTags(tags []string) Option {
o.Tags = tags
}
}

// WithClientSideCaching allows setting the client side caching, enabled by default
// Currently to be used by Rueidis(redis) library only.
func WithClientSideCaching(clientSideCacheExpiration time.Duration) Option {
return func(o *Options) {
o.ClientSideCacheExpiration = clientSideCacheExpiration
}
}
10 changes: 5 additions & 5 deletions store/redis/redis_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ func BenchmarkRedisSet(b *testing.B) {
ctx := context.Background()

store := NewRedis(redis.NewClient(&redis.Options{
Addr: "redis:6379",
}), nil)
Addr: "localhost:6379",
}))

for k := 0.; k <= 10; k++ {
n := int(math.Pow(2, k))
Expand All @@ -34,13 +34,13 @@ func BenchmarkRedisGet(b *testing.B) {
ctx := context.Background()

store := NewRedis(redis.NewClient(&redis.Options{
Addr: "redis:6379",
}), nil)
Addr: "localhost:6379",
}))

key := "test"
value := []byte("value")

store.Set(ctx, key, value, nil)
store.Set(ctx, key, value)

for k := 0.; k <= 10; k++ {
n := int(math.Pow(2, k))
Expand Down
19 changes: 19 additions & 0 deletions store/rueidis/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module github.com/eko/gocache/v4/store/rueidis

go 1.19

require (
github.com/eko/gocache/v4/lib v0.0.0
github.com/golang/mock v1.6.0
github.com/rueian/rueidis v0.0.86
github.com/stretchr/testify v1.8.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/eko/gocache/v4/lib => ../../lib/
53 changes: 53 additions & 0 deletions store/rueidis/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
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/rueian/rueidis v0.0.86 h1:RdzZzYnECg27zwdBHL2JN1XPVfaZclSjx6gzAQuCF/o=
github.com/rueian/rueidis v0.0.86/go.mod h1:LiKWMM/QnILwRfDZIhSIXi4vQqZ/UZy4+/aNkSCt8XA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 h1:yZNXmy+j/JpX19vZkVktWqAo7Gny4PBWYYK3zskGpx4=
golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
138 changes: 138 additions & 0 deletions store/rueidis/rueidis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package rueidis

import (
"context"
"fmt"
"time"

lib_store "github.com/eko/gocache/v4/lib/store"
"github.com/rueian/rueidis"
"github.com/rueian/rueidis/rueidiscompat"
)

const (
// RueidisType represents the storage type as a string value
RueidisType = "rueidis"
// RueidisTagPattern represents the tag pattern to be used as a key in specified storage
RueidisTagPattern = "gocache_tag_%s"

defaultClientSideCacheExpiration = 10 * time.Second
)

// RueidisStore is a store for Redis
type RueidisStore struct {
client rueidis.Client
options *lib_store.Options
cacheCompat rueidiscompat.CacheCompat
compat rueidiscompat.Cmdable
}

// NewRueidis creates a new store to Redis instance(s)
func NewRueidis(client rueidis.Client, options ...lib_store.Option) *RueidisStore {
// defaults client side cache expiration to 10s
appliedOptions := lib_store.ApplyOptions(options...)

if appliedOptions.ClientSideCacheExpiration == 0 {
appliedOptions.ClientSideCacheExpiration = defaultClientSideCacheExpiration
}

return &RueidisStore{
client: client,
cacheCompat: rueidiscompat.NewAdapter(client).Cache(appliedOptions.ClientSideCacheExpiration),
compat: rueidiscompat.NewAdapter(client),
options: appliedOptions,
}
}

// Get returns data stored from a given key
func (s *RueidisStore) Get(ctx context.Context, key any) (any, error) {
object := s.client.DoCache(ctx, s.client.B().Get().Key(key.(string)).Cache(), s.options.ClientSideCacheExpiration)
if object.RedisError() != nil && object.RedisError().IsNil() {
return nil, lib_store.NotFoundWithCause(object.Error())
}
return object, object.Error()
}

// GetWithTTL returns data stored from a given key and its corresponding TTL
func (s *RueidisStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
// get object first
object, err := s.Get(ctx, key)
if err != nil {
return nil, 0, err
}

// get TTL and return
ttl, err := s.cacheCompat.TTL(ctx, key.(string)).Result()
if err != nil {
return nil, 0, err
}

return object, ttl, err
}

// Set defines data in Redis for given key identifier
func (s *RueidisStore) Set(ctx context.Context, key any, value any, options ...lib_store.Option) error {
opts := lib_store.ApplyOptionsWithDefault(s.options, options...)
err := s.compat.Set(ctx, key.(string), value, opts.Expiration).Err()
if err != nil {
return err
}

if tags := opts.Tags; len(tags) > 0 {
s.setTags(ctx, key, tags)
}

return nil
}

func (s *RueidisStore) setTags(ctx context.Context, key any, tags []string) {
for _, tag := range tags {
tagKey := fmt.Sprintf(RueidisTagPattern, tag)
s.compat.SAdd(ctx, tagKey, key.(string))
s.compat.Expire(ctx, tagKey, 720*time.Hour)
}
}

// Delete removes data from Redis for given key identifier
func (s *RueidisStore) Delete(ctx context.Context, key any) error {
_, err := s.compat.Del(ctx, key.(string)).Result()
return err
}

// Invalidate invalidates some cache data in Redis for given options
func (s *RueidisStore) Invalidate(ctx context.Context, options ...lib_store.InvalidateOption) error {
opts := lib_store.ApplyInvalidateOptions(options...)

if tags := opts.Tags; len(tags) > 0 {
for _, tag := range tags {
tagKey := fmt.Sprintf(RueidisTagPattern, tag)

cacheKeys, err := s.cacheCompat.SMembers(ctx, tagKey).Result()
if err != nil {
continue
}

for _, cacheKey := range cacheKeys {
s.Delete(ctx, cacheKey)
}

s.Delete(ctx, tagKey)
}
}

return nil
}

// GetType returns the store type
func (s *RueidisStore) GetType() string {
return RueidisType
}

// Clear resets all data in the store
func (s *RueidisStore) Clear(ctx context.Context) error {
if err := s.compat.FlushAll(ctx).Err(); err != nil {
return err
}

return nil
}
66 changes: 66 additions & 0 deletions store/rueidis/rueidis_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package rueidis

import (
"context"
"fmt"
"math"
"testing"
"time"

lib_store "github.com/eko/gocache/v4/lib/store"
"github.com/rueian/rueidis"
)

func BenchmarkRueidisSet(b *testing.B) {
ctx := context.Background()

ruedisClient, _ := rueidis.NewClient(rueidis.ClientOption{
InitAddress: []string{"localhost:26379"},
Sentinel: rueidis.SentinelOption{
MasterSet: "mymaster",
},
SelectDB: 0,
})

store := NewRueidis(ruedisClient, lib_store.WithExpiration(time.Hour*4))

for k := 0.; k <= 10; k++ {
n := int(math.Pow(2, k))
b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
for i := 0; i < b.N*n; i++ {
key := fmt.Sprintf("test-%d", n)
value := []byte(fmt.Sprintf("value-%d", n))

store.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
}
})
}
}

func BenchmarkRueidisGet(b *testing.B) {
ctx := context.Background()

ruedisClient, _ := rueidis.NewClient(rueidis.ClientOption{
InitAddress: []string{"localhost:26379"},
Sentinel: rueidis.SentinelOption{
MasterSet: "mymaster",
},
SelectDB: 0,
})

store := NewRueidis(ruedisClient, lib_store.WithExpiration(time.Hour*4))

key := "test"
value := []byte("value")

_ = store.Set(ctx, key, value)

for k := 0.; k <= 10; k++ {
n := int(math.Pow(2, k))
b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
for i := 0; i < b.N*n; i++ {
_, _ = store.Get(ctx, key)
}
})
}
}
Loading