forked from mirror/redis
Fix race between Subscribe and resubscribe
This commit is contained in:
parent
6499563e07
commit
191f839e81
|
@ -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.
|
||||||
|
|
52
pubsub.go
52
pubsub.go
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue