forked from mirror/redis
Fix Redis Cluster issue during roll outs of new nodes with same addr (#1914)
* fix: recycle connections in some Redis Cluster scenarios This issue was surfaced in a Cloud Provider solution that used for rolling out new nodes using the same address (hostname) of the nodes that will be replaced in a Redis Cluster, while the former ones once depromoted as Slaves would continue in service during some mintues for redirecting traffic. The solution basically identifies when the connection could be stale since a MOVED response will be returned using the same address (hostname) that is being used by the connection. At that moment we consider the connection as no longer usable forcing to recycle the connection.
This commit is contained in:
parent
507203108a
commit
98bb99ddc2
26
error.go
26
error.go
|
@ -65,7 +65,7 @@ func isRedisError(err error) bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func isBadConn(err error, allowTimeout bool) bool {
|
func isBadConn(err error, allowTimeout bool, addr string) bool {
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
return false
|
return false
|
||||||
|
@ -74,9 +74,19 @@ func isBadConn(err error, allowTimeout bool) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if isRedisError(err) {
|
if isRedisError(err) {
|
||||||
// Close connections in read only state in case domain addr is used
|
switch {
|
||||||
// and domain resolves to a different Redis Server. See #790.
|
case isReadOnlyError(err):
|
||||||
return isReadOnlyError(err)
|
// Close connections in read only state in case domain addr is used
|
||||||
|
// and domain resolves to a different Redis Server. See #790.
|
||||||
|
return true
|
||||||
|
case isMovedSameConnAddr(err, addr):
|
||||||
|
// Close connections when we are asked to move to the same addr
|
||||||
|
// of the connection. Force a DNS resolution when all connections
|
||||||
|
// of the pool are recycled
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowTimeout {
|
if allowTimeout {
|
||||||
|
@ -119,6 +129,14 @@ func isReadOnlyError(err error) bool {
|
||||||
return strings.HasPrefix(err.Error(), "READONLY ")
|
return strings.HasPrefix(err.Error(), "READONLY ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isMovedSameConnAddr(err error, addr string) bool {
|
||||||
|
redisError := err.Error()
|
||||||
|
if !strings.HasPrefix(redisError, "MOVED ") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.HasSuffix(redisError, addr)
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
type timeoutError interface {
|
type timeoutError interface {
|
||||||
|
|
|
@ -141,7 +141,7 @@ func (c *PubSub) releaseConn(ctx context.Context, cn *pool.Conn, err error, allo
|
||||||
if c.cn != cn {
|
if c.cn != cn {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if isBadConn(err, allowTimeout) {
|
if isBadConn(err, allowTimeout, c.opt.Addr) {
|
||||||
c.reconnect(ctx, err)
|
c.reconnect(ctx, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
redis.go
2
redis.go
|
@ -261,7 +261,7 @@ func (c *baseClient) releaseConn(ctx context.Context, cn *pool.Conn, err error)
|
||||||
c.opt.Limiter.ReportResult(err)
|
c.opt.Limiter.ReportResult(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isBadConn(err, false) {
|
if isBadConn(err, false, c.opt.Addr) {
|
||||||
c.connPool.Remove(ctx, cn, err)
|
c.connPool.Remove(ctx, cn, err)
|
||||||
} else {
|
} else {
|
||||||
c.connPool.Put(ctx, cn)
|
c.connPool.Put(ctx, cn)
|
||||||
|
|
Loading…
Reference in New Issue