Skip to content

Commit

Permalink
Merge fb6f7da into 1bf0f67
Browse files Browse the repository at this point in the history
  • Loading branch information
tyzhnenko committed Oct 13, 2019
2 parents 1bf0f67 + fb6f7da commit f6cd21d
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 1 deletion.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module github.com/go-pkgz/lcw

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-redis/redis v6.15.6+incompatible
github.com/hashicorp/golang-lru v0.5.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.8.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
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/go-redis/redis v6.15.6+incompatible h1:H9evprGPLI8+ci7fxQx6WNZHJSb7be8FqJQRhdQZ5Sg=
github.com/go-redis/redis v6.15.6+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
Expand Down
2 changes: 1 addition & 1 deletion lru_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/pkg/errors"
)

// LruCache wraps lru.LruCache with laoding cache Get and size limits
// LruCache wraps lru.LruCache with loading cache Get and size limits
type LruCache struct {
options
CacheStat
Expand Down
14 changes: 14 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package lcw
import (
"errors"
"time"

"github.com/go-redis/redis"
)

type options struct {
Expand All @@ -12,6 +14,7 @@ type options struct {
maxCacheSize int64
ttl time.Duration
onEvicted func(key string, value Value)
redisOptions *redis.Options
}

// Option func type
Expand Down Expand Up @@ -84,3 +87,14 @@ func OnEvicted(fn func(key string, value Value)) Option {
return nil
}
}

// RedisOptions functional option defines duration.
// Works for RedisCache only
func RedisOptions(Addr string, Password string, DB int) Option {
return func(o *options) error {
o.redisOptions.Addr = Addr
o.redisOptions.Password = Password
o.redisOptions.DB = DB
return nil
}
}
124 changes: 124 additions & 0 deletions redis_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package lcw

import (
"sync/atomic"
"time"

"github.com/go-redis/redis"
"github.com/pkg/errors"
)

// RedisCache implements LoadingCache for Redis.
type RedisCache struct {
options
CacheStat
backend *redis.Client
}

// NewRedisCache makes Redis LoadingCache implementation.
func NewRedisCache(opts ...Option) (*RedisCache, error) {

res := RedisCache{
options: options{
ttl: 5 * time.Minute,
},
}
for _, opt := range opts {
if err := opt(&res.options); err != nil {
return nil, errors.Wrap(err, "failed to set cache option")
}
}

res.backend = redis.NewClient(res.redisOptions)

return &res, nil
}

// Get gets value by key or load with fn if not found in cache
func (c *RedisCache) Get(key string, fn func() (Value, error)) (data Value, err error) {

v, ok := c.backend.Get(key).Result()
if ok == nil {
atomic.AddInt64(&c.Hits, 1)
return v, nil
}
if ok == redis.Nil {
if data, err = fn(); err != nil {
atomic.AddInt64(&c.Errors, 1)
return data, err
}
} else if ok != nil {
atomic.AddInt64(&c.Errors, 1)
return v, ok
}
atomic.AddInt64(&c.Misses, 1)

if c.allowed(key, data) {
c.backend.Set(key, data, c.ttl)
}
return data, nil
}

// Invalidate removes keys with passed predicate fn, i.e. fn(key) should be true to get evicted
func (c *RedisCache) Invalidate(fn func(key string) bool) {
for _, key := range c.backend.Keys("*").Val() { // Keys() returns copy of cache's key, safe to remove directly
if fn(key) {
c.backend.Del(key)
}
}
}

// Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key.
func (c *RedisCache) Peek(key string) (Value, bool) {
ret, err := c.backend.Get(key).Result()
if err != nil {
return nil, false
}
return ret, true
}

// Purge clears the cache completely.
func (c *RedisCache) Purge() {
c.backend.FlushDB()

}

// Delete cache item by key
func (c *RedisCache) Delete(key string) {
c.backend.Del(key)
}

// Stat returns cache statistics
func (c *RedisCache) Stat() CacheStat {
return CacheStat{
Hits: c.Hits,
Misses: c.Misses,
Size: c.size(),
Keys: c.keys(),
Errors: c.Errors,
}
}

func (c *RedisCache) size() int64 {
return 0
}

func (c *RedisCache) keys() int {
return int(c.backend.DBSize().Val())
}

func (c *RedisCache) allowed(key string, data Value) bool {
if c.backend.DBSize().Val() >= int64(c.maxKeys) {
return false
}
if c.maxKeySize > 0 && len(key) > c.maxKeySize {
return false
}
if s, ok := data.(Sizer); ok {
// Maximum allowed value size in Redis
if s.Size() >= (512 * 1024 * 1024) {
return false
}
}
return true
}

0 comments on commit f6cd21d

Please sign in to comment.