3
0

upd(sam): refactor pubsub to repository

This commit is contained in:
Tit Petric
2018-09-11 18:12:41 +02:00
parent 67d342c8fc
commit 33bdf429a5
4 changed files with 128 additions and 116 deletions

View File

@@ -4,8 +4,8 @@ import (
"log"
"os"
"github.com/crusttech/crust/crm"
"github.com/crusttech/crust/auth"
"github.com/crusttech/crust/crm"
"github.com/crusttech/crust/rbac"
)

120
sam/repository/pubsub.go Normal file
View File

@@ -0,0 +1,120 @@
package repository
import (
"context"
"time"
"github.com/gomodule/redigo/redis"
"github.com/pkg/errors"
"github.com/crusttech/crust/config"
)
type PubSub struct {
ctx context.Context
config *config.PubSub
}
func (PubSub) New(ctx context.Context) (*PubSub, error) {
if err := flags.PubSub.Validate(); err != nil {
return nil, err
}
return &PubSub{ctx, flags.PubSub}, nil
}
func (ps *PubSub) With(ctx context.Context) *PubSub {
return &PubSub{ctx, ps.config}
}
func (ps *PubSub) dial() (redis.Conn, error) {
return redis.Dial(
"tcp",
flags.PubSub.RedisAddr,
redis.DialReadTimeout(ps.config.PingTimeout+ps.config.Timeout),
redis.DialWriteTimeout(ps.config.Timeout),
)
}
func (ps *PubSub) Subscribe(onStart func() error, onMessage func(channel string, payload []byte) error, channels ...string) error {
if len(channels) == 0 {
return errors.New("Need to subscribe at least to one channel")
}
// main redis connection
conn, err := ps.dial()
if err != nil {
return err
}
defer conn.Close()
// pubsub object
psc := redis.PubSubConn{Conn: conn}
if err := psc.Subscribe(redis.Args{}.AddFlat(channels)...); err != nil {
return err
}
done := make(chan error, 1)
// Start a goroutine to receive notifications from the server.
go func() {
for {
switch n := psc.Receive().(type) {
case error:
done <- n
return
case redis.Message:
if err := onMessage(n.Channel, n.Data); err != nil {
done <- err
return
}
case redis.Subscription:
switch n.Count {
case len(channels):
// Notify application when all channels are subscribed.
if err := onStart(); err != nil {
done <- err
return
}
case 0:
// Return from the goroutine when all channels are unsubscribed.
done <- nil
return
}
}
}
}()
ticker := time.NewTicker(ps.config.PingPeriod)
defer ticker.Stop()
cleanup := func(err error) error {
psc.Unsubscribe()
return err
}
for {
select {
case <-ticker.C:
if err := psc.Ping(""); err != nil {
return cleanup(err)
}
case <-ps.ctx.Done():
return cleanup(ps.ctx.Err())
case err := <-done:
return err
}
}
}
func (ps *PubSub) Publish(channel, payload string) error {
// main redis connection
conn, err := ps.dial()
if err != nil {
return err
}
defer conn.Close()
// publish payload on channel
_, err = conn.Do("PUBLISH", channel, payload)
return err
}

View File

@@ -2,126 +2,18 @@ package service
import (
"context"
"time"
"github.com/gomodule/redigo/redis"
"github.com/pkg/errors"
"github.com/crusttech/crust/config"
"github.com/crusttech/crust/sam/repository"
)
type PubSub struct {
ctx context.Context
redisServerAddr string
healthCheckInterval time.Duration
*repository.PubSub
}
func (ps PubSub) New(config *config.PubSub, ctx context.Context) (*PubSub, error) {
if err := config.Validate(); err != nil {
func (PubSub) New(ctx context.Context) (*PubSub, error) {
rpo, err := repository.PubSub{}.New(ctx)
if err != nil {
return nil, err
}
return &PubSub{
ctx: ctx,
redisServerAddr: config.RedisAddr,
healthCheckInterval: time.Minute,
}, nil
}
func (ps *PubSub) With(ctx context.Context) *PubSub {
return &PubSub{
ctx: ctx,
healthCheckInterval: ps.healthCheckInterval,
}
}
func (ps *PubSub) dial() (redis.Conn, error) {
readTimeout := redis.DialReadTimeout(ps.healthCheckInterval + 10*time.Second)
writeTimeout := redis.DialWriteTimeout(10 * time.Second)
return redis.Dial("tcp", ps.redisServerAddr, readTimeout, writeTimeout)
}
func (ps *PubSub) Subscribe(onStart func() error, onMessage func(channel string, payload []byte) error, channels ...string) error {
if len(channels) == 0 {
return errors.New("Need to subscribe at least to one channel")
}
// main redis connection
conn, err := ps.dial()
if err != nil {
return err
}
defer conn.Close()
// pubsub object
psc := redis.PubSubConn{Conn: conn}
if err := psc.Subscribe(redis.Args{}.AddFlat(channels)...); err != nil {
return err
}
done := make(chan error, 1)
// Start a goroutine to receive notifications from the server.
go func() {
for {
switch n := psc.Receive().(type) {
case error:
done <- n
return
case redis.Message:
if err := onMessage(n.Channel, n.Data); err != nil {
done <- err
return
}
case redis.Subscription:
switch n.Count {
case len(channels):
// Notify application when all channels are subscribed.
if err := onStart(); err != nil {
done <- err
return
}
case 0:
// Return from the goroutine when all channels are unsubscribed.
done <- nil
return
}
}
}
}()
ticker := time.NewTicker(ps.healthCheckInterval)
defer ticker.Stop()
cleanup := func(err error) error {
psc.Unsubscribe()
return err
}
for {
select {
case <-ticker.C:
if err := psc.Ping(""); err != nil {
return cleanup(err)
}
case <-ps.ctx.Done():
return cleanup(ps.ctx.Err())
case err := <-done:
return err
}
}
}
func (ps *PubSub) Publish(channel, payload string) error {
// main redis connection
conn, err := ps.dial()
if err != nil {
return err
}
defer conn.Close()
// publish payload on channel
_, err = conn.Do("PUBLISH", channel, payload)
return err
return &PubSub{rpo}, nil
}

View File

@@ -67,7 +67,7 @@ func (eq *eventQueue) feedSessions(ctx context.Context, config *repository.Flags
// feed events from redis into newMessageEvent channel
if config.PubSub.Mode == "redis" {
pubsub, err := service.PubSub{}.New(config.PubSub, ctx)
pubsub, err := service.PubSub{}.New(ctx)
if err != nil {
log.Printf("PubSub: Error when starting in mode=redis, %+v\n", err)
log.Println("PubSub: Reverting to polling mode")