-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #26 from go-pkgz/pubsub
Basic support for distributed cache for LRU and Expirable caches
- Loading branch information
Showing
15 changed files
with
453 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,9 @@ jobs: | |
- name: checkout | ||
uses: actions/checkout@v2 | ||
|
||
- name: start Redis | ||
uses: supercharge/[email protected] | ||
|
||
- name: build and test | ||
run: | | ||
go get -v | ||
|
@@ -29,6 +32,7 @@ jobs: | |
env: | ||
GO111MODULE: "on" | ||
TZ: "America/Chicago" | ||
ENABLE_REDIS_TESTS: "true" | ||
|
||
- name: install golangci-lint and goveralls | ||
run: | | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package eventbus | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestNopPubSub(t *testing.T) { | ||
nopPubSub := NopPubSub{} | ||
assert.NoError(t, nopPubSub.Subscribe(nil)) | ||
assert.NoError(t, nopPubSub.Publish("", "")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
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 { | ||
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package eventbus | ||
|
||
import ( | ||
"math/rand" | ||
"os" | ||
"strconv" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestNewRedisPubSub_Error(t *testing.T) { | ||
redisPubSub, err := NewRedisPubSub("127.0.0.1:99999", "test") | ||
require.Error(t, err) | ||
require.Nil(t, redisPubSub) | ||
} | ||
|
||
func TestRedisPubSub(t *testing.T) { | ||
if _, ok := os.LookupEnv("ENABLE_REDIS_TESTS"); !ok { | ||
t.Skip("ENABLE_REDIS_TESTS env variable is not set, not expecting Redis to be ready at 127.0.0.1:6379") | ||
} | ||
|
||
channel := "lcw-test-" + strconv.Itoa(rand.Intn(1000000)) | ||
redisPubSub, err := NewRedisPubSub("127.0.0.1:6379", channel) | ||
require.NoError(t, err) | ||
require.NotNil(t, redisPubSub) | ||
var called []string | ||
assert.Nil(t, redisPubSub.Subscribe(func(fromID, key string) { | ||
called = append(called, fromID, key) | ||
})) | ||
assert.NoError(t, redisPubSub.Publish("test_fromID", "$test$key$")) | ||
// Sleep which waits for Subscribe goroutine to pick up published changes | ||
time.Sleep(time.Second) | ||
assert.NoError(t, redisPubSub.Close()) | ||
assert.Equal(t, []string{"test_fromID", "$test$key$"}, called) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.