bump go-pkgz/lcw to v0.7.1

This commit is contained in:
Dmitry Verkhoturov
2020-06-25 17:28:33 -05:00
committed by Umputun
parent df9c05b490
commit ebf379f4d6
13 changed files with 180 additions and 15 deletions
+2 -2
View File
@@ -45,12 +45,12 @@ github.com/go-chi/chi v4.1.1+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxm
github.com/go-chi/cors v1.1.1/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I=
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
github.com/go-pkgz/auth v0.10.2/go.mod h1:w4Z1qaYvuh2P3T2gNh0f8GcKCH0HHAoQtQ8iv+9+WGg=
github.com/go-pkgz/auth v0.11.0/go.mod h1:NzVqlTW0E9JXVdAaWRq81XZjICgHnNaNdUfE3CbS2T4=
github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6dksJLXQDc=
github.com/go-pkgz/expirable-cache v0.0.3/go.mod h1:+IauqN00R2FqNRLCLA+X5YljQJrwB179PfiAoMPlTlQ=
github.com/go-pkgz/jrpc v0.2.0 h1:CLy/eZyekjraVrxZV18N2R1mYLMJ/nWrgdfyIOGPY/E=
github.com/go-pkgz/jrpc v0.2.0/go.mod h1:wd8vtQ4CgtCnuqua6x2b1SKIgv0VSOh5Dn0uUITbiUE=
github.com/go-pkgz/lcw v0.6.1/go.mod h1:vovP88gZLeuIWn5cm0NlgPYFyGGkv3m2OcKMOOaHhj0=
github.com/go-pkgz/lcw v0.7.1/go.mod h1:3P6g9QrJsDePXEMe42ywO+tW08L17tBJGwIDdI7lZ6g=
github.com/go-pkgz/lgr v0.7.0 h1:S/AAPwt/RE9a5mNJskA7dGVp+Dq6SMIW6LYjG3ITxY8=
github.com/go-pkgz/lgr v0.7.0/go.mod h1:yMgxU+GobMRJgIEbSzDKy/67W18S7qmGx/7BVL5AB8Q=
github.com/go-pkgz/repeater v1.1.3/go.mod h1:hVTavuO5x3Gxnu8zW7d6sQBfAneKV8X2FjU48kGfpKw=
+1 -1
View File
@@ -14,7 +14,7 @@ require (
github.com/go-chi/render v1.0.1
github.com/go-pkgz/auth v0.11.0
github.com/go-pkgz/jrpc v0.2.0
github.com/go-pkgz/lcw v0.6.1
github.com/go-pkgz/lcw v0.7.1
github.com/go-pkgz/lgr v0.7.0
github.com/go-pkgz/repeater v1.1.3
github.com/go-pkgz/rest v1.5.0
+7 -2
View File
@@ -62,8 +62,8 @@ github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6d
github.com/go-pkgz/expirable-cache v0.0.3/go.mod h1:+IauqN00R2FqNRLCLA+X5YljQJrwB179PfiAoMPlTlQ=
github.com/go-pkgz/jrpc v0.2.0 h1:CLy/eZyekjraVrxZV18N2R1mYLMJ/nWrgdfyIOGPY/E=
github.com/go-pkgz/jrpc v0.2.0/go.mod h1:wd8vtQ4CgtCnuqua6x2b1SKIgv0VSOh5Dn0uUITbiUE=
github.com/go-pkgz/lcw v0.6.1 h1:hb1v9oFaP6MiGSvROoVTiTmcyU/qHzGebUOhA74l4xU=
github.com/go-pkgz/lcw v0.6.1/go.mod h1:vovP88gZLeuIWn5cm0NlgPYFyGGkv3m2OcKMOOaHhj0=
github.com/go-pkgz/lcw v0.7.1 h1:2Q2k1am6xb2KgxZG84kL072q66rlWFUR+MvgPcDcJXA=
github.com/go-pkgz/lcw v0.7.1/go.mod h1:3P6g9QrJsDePXEMe42ywO+tW08L17tBJGwIDdI7lZ6g=
github.com/go-pkgz/lgr v0.7.0 h1:S/AAPwt/RE9a5mNJskA7dGVp+Dq6SMIW6LYjG3ITxY8=
github.com/go-pkgz/lgr v0.7.0/go.mod h1:yMgxU+GobMRJgIEbSzDKy/67W18S7qmGx/7BVL5AB8Q=
github.com/go-pkgz/repeater v1.1.3 h1:q6+JQF14ESSy28Dd7F+wRelY4F+41HJ0LEy/szNnMiE=
@@ -157,6 +157,7 @@ github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kN
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -203,6 +204,7 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -308,6 +310,7 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -327,6 +330,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -338,6 +342,7 @@ gopkg.in/oauth2.v3 v3.12.0/go.mod h1:XEYgKqWX095YiPT+Aw5y3tCn+7/FMnlTFKrupgSiJ3I
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+6 -3
View File
@@ -27,8 +27,11 @@ Main features:
## Usage
```
cache := lcw.NewLruCache(lcw.MaxKeys(500), lcw.MaxCacheSize(65536), lcw.MaxValSize(200), lcw.MaxKeySize(32))
```go
cache, err := lcw.NewLruCache(lcw.MaxKeys(500), lcw.MaxCacheSize(65536), lcw.MaxValSize(200), lcw.MaxKeySize(32))
if err != nil {
panic("failed to create cache")
}
defer cache.Close()
val, err := cache.Get("key123", func() (lcw.Value, error) {
@@ -59,7 +62,7 @@ Cache can be created with URIs:
1. Key is not a string, but a composed type made from partition, key-id and list of scopes (tags).
1. Value type limited to `[]byte`
1. Added `Flush` method for scoped/tagged invalidation of multiple records in a given partition
1. A simplified interface with Get, Stat and Flush only.
1. A simplified interface with Get, Stat, Flush and Close only.
## Details
+3
View File
@@ -6,6 +6,8 @@
// 3 flavors of cache provided - NoP (do-nothing cache), ExpirableCache (TTL based), and LruCache
package lcw
//go:generate sh -c "mockery -inpkg -name LoadingCache -print > /tmp/cache-mock.tmp && mv /tmp/cache-mock.tmp cache_mock.go"
import (
"fmt"
)
@@ -81,3 +83,4 @@ func (n *Nop) Stat() CacheStat {
func (n *Nop) Close() error {
return nil
}
+24
View File
@@ -0,0 +1,24 @@
// Package eventbus provides PubSub interface used for distributed cache invalidation,
// as well as NopPubSub and RedisPubSub implementations.
package eventbus
// PubSub interface is used for distributed cache invalidation.
// Publish is called on each entry invalidation,
// Subscribe is used for subscription for these events.
type PubSub interface {
Publish(fromID, key string) error
Subscribe(fn func(fromID, key string)) error
}
// NopPubSub implements default do-nothing pub-sub (event bus)
type NopPubSub struct{}
// Subscribe does nothing for NopPubSub
func (n *NopPubSub) Subscribe(fn func(fromID, key string)) error {
return nil
}
// Publish does nothing for NopPubSub
func (n *NopPubSub) Publish(fromID, key string) error {
return nil
}
+72
View File
@@ -0,0 +1,72 @@
package eventbus
import (
"strings"
"time"
"github.com/go-redis/redis/v7"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
)
// NewRedisPubSub creates new RedisPubSub with given parameters.
// Returns an error in case of problems with creating PubSub client for specified channel.
func NewRedisPubSub(addr, channel string) (*RedisPubSub, error) {
client := redis.NewClient(&redis.Options{Addr: addr})
pubSub := client.Subscribe(channel)
// wait for subscription to be created and ignore the message
if _, err := pubSub.Receive(); err != nil {
_ = client.Close()
return nil, errors.Wrapf(err, "problem subscribing to channel %s on address %s", channel, addr)
}
return &RedisPubSub{client: client, pubSub: pubSub, channel: channel, done: make(chan struct{})}, nil
}
// RedisPubSub provides Redis implementation for PubSub interface
type RedisPubSub struct {
client *redis.Client
pubSub *redis.PubSub
channel string
done chan struct{}
}
// Subscribe calls provided function on subscription channel provided on new RedisPubSub instance creation.
// Should not be called more than once. Spawns a goroutine and does not return an error.
func (m *RedisPubSub) Subscribe(fn func(fromID, key string)) error {
go func(done <-chan struct{}, pubsub *redis.PubSub) {
for {
select {
case <-done:
return
default:
}
msg, err := pubsub.ReceiveTimeout(time.Second * 10)
if err != nil {
continue
}
// Process the message
if msg, ok := msg.(*redis.Message); ok {
payload := strings.Split(msg.Payload, "$")
fn(payload[0], strings.Join(payload[1:], "$"))
}
}
}(m.done, m.pubSub)
return nil
}
// Publish publishes provided message to channel provided on new RedisPubSub instance creation
func (m *RedisPubSub) Publish(fromID, key string) error {
return m.client.Publish(m.channel, fromID+"$"+key).Err()
}
// Close cleans up running goroutines and closes Redis clients
func (m *RedisPubSub) Close() error {
close(m.done)
errs := new(multierror.Error)
errs = multierror.Append(errs, errors.Wrap(m.pubSub.Close(), "problem closing pubSub client"))
errs = multierror.Append(errs, errors.Wrap(m.client.Close(), "problem closing redis client"))
return errs.ErrorOrNil()
}
+20
View File
@@ -4,8 +4,10 @@ import (
"sync/atomic"
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/go-pkgz/lcw/eventbus"
"github.com/go-pkgz/lcw/internal/cache"
)
@@ -14,6 +16,7 @@ type ExpirableCache struct {
options
CacheStat
currentSize int64
id string
backend *cache.LoadingCache
}
@@ -24,7 +27,9 @@ func NewExpirableCache(opts ...Option) (*ExpirableCache, error) {
maxKeys: 1000,
maxValueSize: 0,
ttl: 5 * time.Minute,
eventBus: &eventbus.NopPubSub{},
},
id: uuid.New().String(),
}
for _, opt := range opts {
@@ -33,6 +38,10 @@ func NewExpirableCache(opts ...Option) (*ExpirableCache, error) {
}
}
if err := res.eventBus.Subscribe(res.onBusEvent); err != nil {
return nil, errors.Wrapf(err, "can't subscribe to event bus")
}
backend, err := cache.NewLoadingCache(
cache.MaxKeys(res.maxKeys),
cache.TTL(res.ttl),
@@ -45,6 +54,10 @@ func NewExpirableCache(opts ...Option) (*ExpirableCache, error) {
size := s.Size()
atomic.AddInt64(&res.currentSize, -1*int64(size))
}
// ignore the error on Publish as we don't have log inside the module and
// there is no other way to handle it: we publish the cache invalidation
// and hope for the best
_ = res.eventBus.Publish(res.id, key)
}),
)
if err != nil {
@@ -128,6 +141,13 @@ func (c *ExpirableCache) Close() error {
return nil
}
// onBusEvent reacts on invalidation message triggered by event bus from another cache instance
func (c *ExpirableCache) onBusEvent(id, key string) {
if id != c.id {
c.backend.Invalidate(key)
}
}
func (c *ExpirableCache) size() int64 {
return atomic.LoadInt64(&c.currentSize)
}
+1
View File
@@ -3,6 +3,7 @@ module github.com/go-pkgz/lcw
require (
github.com/alicebob/miniredis/v2 v2.11.4
github.com/go-redis/redis/v7 v7.2.0
github.com/google/uuid v1.1.1
github.com/hashicorp/go-multierror v1.1.0
github.com/hashicorp/golang-lru v0.5.4
github.com/pkg/errors v0.9.1
+2
View File
@@ -16,6 +16,8 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3 h1:6amM4HsNPOvMLVc2ZnyqrjeQ92YAVWn7T4WBKK87inY=
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
+29 -6
View File
@@ -3,8 +3,11 @@ package lcw
import (
"sync/atomic"
"github.com/google/uuid"
lru "github.com/hashicorp/golang-lru"
"github.com/pkg/errors"
"github.com/go-pkgz/lcw/eventbus"
)
// LruCache wraps lru.LruCache with loading cache Get and size limits
@@ -13,6 +16,7 @@ type LruCache struct {
CacheStat
backend *lru.Cache
currentSize int64
id string // uuid identifying cache instance
}
// NewLruCache makes LRU LoadingCache implementation, 1000 max keys by default
@@ -21,7 +25,9 @@ func NewLruCache(opts ...Option) (*LruCache, error) {
options: options{
maxKeys: 1000,
maxValueSize: 0,
eventBus: &eventbus.NopPubSub{},
},
id: uuid.New().String(),
}
for _, opt := range opts {
if err := opt(&res.options); err != nil {
@@ -29,23 +35,33 @@ func NewLruCache(opts ...Option) (*LruCache, error) {
}
}
err := res.init()
return &res, err
}
func (c *LruCache) init() error {
if err := c.eventBus.Subscribe(c.onBusEvent); err != nil {
return errors.Wrapf(err, "can't subscribe to event bus")
}
onEvicted := func(key interface{}, value interface{}) {
if res.onEvicted != nil {
res.onEvicted(key.(string), value)
if c.onEvicted != nil {
c.onEvicted(key.(string), value)
}
if s, ok := value.(Sizer); ok {
size := s.Size()
atomic.AddInt64(&res.currentSize, -1*int64(size))
atomic.AddInt64(&c.currentSize, -1*int64(size))
}
_ = c.eventBus.Publish(c.id, key.(string)) // signal invalidation to other nodes
}
var err error
// OnEvicted called automatically for expired and manually deleted
if res.backend, err = lru.NewWithEvict(res.maxKeys, onEvicted); err != nil {
return nil, errors.Wrap(err, "failed to make lru cache backend")
if c.backend, err = lru.NewWithEvict(c.maxKeys, onEvicted); err != nil {
return errors.Wrap(err, "failed to make lru cache backend")
}
return &res, nil
return nil
}
// Get gets value by key or load with fn if not found in cache
@@ -131,6 +147,13 @@ func (c *LruCache) Close() error {
return nil
}
// onBusEvent reacts on invalidation message triggered by event bus from another cache instance
func (c *LruCache) onBusEvent(id, key string) {
if id != c.id && c.backend.Contains(key) { // prevent reaction on event from this cache
c.backend.Remove(key)
}
}
func (c *LruCache) size() int64 {
return atomic.LoadInt64(&c.currentSize)
}
+11
View File
@@ -3,6 +3,8 @@ package lcw
import (
"errors"
"time"
"github.com/go-pkgz/lcw/eventbus"
)
type options struct {
@@ -12,6 +14,7 @@ type options struct {
maxCacheSize int64
ttl time.Duration
onEvicted func(key string, value Value)
eventBus eventbus.PubSub
}
// Option func type
@@ -84,3 +87,11 @@ func OnEvicted(fn func(key string, value Value)) Option {
return nil
}
}
// EventBus sets PubSub for distributed cache invalidation
func EventBus(pubSub eventbus.PubSub) Option {
return func(o *options) error {
o.eventBus = pubSub
return nil
}
}
+2 -1
View File
@@ -86,9 +86,10 @@ github.com/go-pkgz/expirable-cache
# github.com/go-pkgz/jrpc v0.2.0
## explicit
github.com/go-pkgz/jrpc
# github.com/go-pkgz/lcw v0.6.1
# github.com/go-pkgz/lcw v0.7.1
## explicit
github.com/go-pkgz/lcw
github.com/go-pkgz/lcw/eventbus
github.com/go-pkgz/lcw/internal/cache
# github.com/go-pkgz/lgr v0.7.0
## explicit