Fix race between Subscribe and resubscribe

This commit is contained in:
Vladimir Mihailenco 2017-04-17 16:05:01 +03:00
parent 6499563e07
commit 191f839e81
2 changed files with 36 additions and 18 deletions

View File

@ -47,7 +47,7 @@ type Options struct {
WriteTimeout time.Duration WriteTimeout time.Duration
// Maximum number of socket connections. // Maximum number of socket connections.
// Default is 100 connections. // Default is 10 connections.
PoolSize int PoolSize int
// Amount of time client waits for connection if all connections // Amount of time client waits for connection if all connections
// are busy before returning an error. // are busy before returning an error.

View File

@ -22,6 +22,7 @@ type PubSub struct {
cmd *Cmd cmd *Cmd
subMu sync.Mutex
channels []string channels []string
patterns []string patterns []string
} }
@ -33,23 +34,32 @@ func (c *PubSub) conn() (*pool.Conn, error) {
} }
if isNew { if isNew {
c.resubscribe() if err := c.resubscribe(); err != nil {
internal.Logf("resubscribe failed: %s", err)
}
} }
return cn, nil return cn, nil
} }
func (c *PubSub) resubscribe() { func (c *PubSub) resubscribe() error {
if len(c.channels) > 0 { c.subMu.Lock()
if err := c.subscribe("subscribe", c.channels...); err != nil { channels := c.channels
internal.Logf("Subscribe failed: %s", err) patterns := c.patterns
c.subMu.Unlock()
var firstErr error
if len(channels) > 0 {
if err := c.subscribe("subscribe", channels...); err != nil && firstErr == nil {
firstErr = err
} }
} }
if len(c.patterns) > 0 { if len(patterns) > 0 {
if err := c.subscribe("psubscribe", c.patterns...); err != nil { if err := c.subscribe("psubscribe", patterns...); err != nil && firstErr == nil {
internal.Logf("PSubscribe failed: %s", err) firstErr = err
} }
} }
return firstErr
} }
func (c *PubSub) _conn() (*pool.Conn, bool, error) { func (c *PubSub) _conn() (*pool.Conn, bool, error) {
@ -91,11 +101,15 @@ func (c *PubSub) subscribe(redisCmd string, channels ...string) error {
} }
cmd := NewSliceCmd(args...) cmd := NewSliceCmd(args...)
cn, err := c.conn() cn, isNew, err := c._conn()
if err != nil { if err != nil {
return err return err
} }
if isNew {
return c.resubscribe()
}
cn.SetWriteTimeout(c.base.opt.WriteTimeout) cn.SetWriteTimeout(c.base.opt.WriteTimeout)
err = writeCmd(cn, cmd) err = writeCmd(cn, cmd)
c.putConn(cn, err) c.putConn(cn, err)
@ -104,32 +118,36 @@ func (c *PubSub) subscribe(redisCmd string, channels ...string) error {
// Subscribes the client to the specified channels. // Subscribes the client to the specified channels.
func (c *PubSub) Subscribe(channels ...string) error { func (c *PubSub) Subscribe(channels ...string) error {
err := c.subscribe("subscribe", channels...) c.subMu.Lock()
c.channels = appendIfNotExists(c.channels, channels...) c.channels = appendIfNotExists(c.channels, channels...)
return err c.subMu.Unlock()
return c.subscribe("subscribe", channels...)
} }
// Subscribes the client to the given patterns. // Subscribes the client to the given patterns.
func (c *PubSub) PSubscribe(patterns ...string) error { func (c *PubSub) PSubscribe(patterns ...string) error {
err := c.subscribe("psubscribe", patterns...) c.subMu.Lock()
c.patterns = appendIfNotExists(c.patterns, patterns...) c.patterns = appendIfNotExists(c.patterns, patterns...)
return err c.subMu.Unlock()
return c.subscribe("psubscribe", patterns...)
} }
// Unsubscribes the client from the given channels, or from all of // Unsubscribes the client from the given channels, or from all of
// them if none is given. // them if none is given.
func (c *PubSub) Unsubscribe(channels ...string) error { func (c *PubSub) Unsubscribe(channels ...string) error {
err := c.subscribe("unsubscribe", channels...) c.subMu.Lock()
c.channels = remove(c.channels, channels...) c.channels = remove(c.channels, channels...)
return err c.subMu.Unlock()
return c.subscribe("unsubscribe", channels...)
} }
// Unsubscribes the client from the given patterns, or from all of // Unsubscribes the client from the given patterns, or from all of
// them if none is given. // them if none is given.
func (c *PubSub) PUnsubscribe(patterns ...string) error { func (c *PubSub) PUnsubscribe(patterns ...string) error {
err := c.subscribe("punsubscribe", patterns...) c.subMu.Lock()
c.patterns = remove(c.patterns, patterns...) c.patterns = remove(c.patterns, patterns...)
return err c.subMu.Unlock()
return c.subscribe("punsubscribe", patterns...)
} }
func (c *PubSub) Close() error { func (c *PubSub) Close() error {