mirror of
https://github.com/redis/go-redis.git
synced 2025-08-07 12:42:55 +03:00
Select random node when there are no keys.
This commit is contained in:
162
cluster.go
162
cluster.go
@@ -12,6 +12,72 @@ import (
|
||||
"gopkg.in/redis.v4/internal/pool"
|
||||
)
|
||||
|
||||
// ClusterOptions are used to configure a cluster client and should be
|
||||
// passed to NewClusterClient.
|
||||
type ClusterOptions struct {
|
||||
// A seed list of host:port addresses of cluster nodes.
|
||||
Addrs []string
|
||||
|
||||
// The maximum number of retries before giving up. Command is retried
|
||||
// on network errors and MOVED/ASK redirects.
|
||||
// Default is 16.
|
||||
MaxRedirects int
|
||||
|
||||
// Enables read queries for a connection to a Redis Cluster slave node.
|
||||
ReadOnly bool
|
||||
|
||||
// Enables routing read-only queries to the closest master or slave node.
|
||||
RouteByLatency bool
|
||||
|
||||
// Following options are copied from Options struct.
|
||||
|
||||
Password string
|
||||
|
||||
DialTimeout time.Duration
|
||||
ReadTimeout time.Duration
|
||||
WriteTimeout time.Duration
|
||||
|
||||
// PoolSize applies per cluster node and not for the whole cluster.
|
||||
PoolSize int
|
||||
PoolTimeout time.Duration
|
||||
IdleTimeout time.Duration
|
||||
IdleCheckFrequency time.Duration
|
||||
}
|
||||
|
||||
func (opt *ClusterOptions) init() {
|
||||
if opt.MaxRedirects == -1 {
|
||||
opt.MaxRedirects = 0
|
||||
} else if opt.MaxRedirects == 0 {
|
||||
opt.MaxRedirects = 16
|
||||
}
|
||||
|
||||
if opt.RouteByLatency {
|
||||
opt.ReadOnly = true
|
||||
}
|
||||
}
|
||||
|
||||
func (opt *ClusterOptions) clientOptions() *Options {
|
||||
const disableIdleCheck = -1
|
||||
|
||||
return &Options{
|
||||
Password: opt.Password,
|
||||
ReadOnly: opt.ReadOnly,
|
||||
|
||||
DialTimeout: opt.DialTimeout,
|
||||
ReadTimeout: opt.ReadTimeout,
|
||||
WriteTimeout: opt.WriteTimeout,
|
||||
|
||||
PoolSize: opt.PoolSize,
|
||||
PoolTimeout: opt.PoolTimeout,
|
||||
IdleTimeout: opt.IdleTimeout,
|
||||
|
||||
// IdleCheckFrequency is not copied to disable reaper
|
||||
IdleCheckFrequency: disableIdleCheck,
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type clusterNode struct {
|
||||
Client *Client
|
||||
Latency time.Duration
|
||||
@@ -36,8 +102,8 @@ type ClusterClient struct {
|
||||
slots [][]*clusterNode
|
||||
closed bool
|
||||
|
||||
cmdsInfo map[string]*CommandInfo
|
||||
cmdsInfoOnce *sync.Once
|
||||
cmdsInfo map[string]*CommandInfo
|
||||
|
||||
// Reports where slots reloading is in progress.
|
||||
reloading uint32
|
||||
@@ -81,19 +147,22 @@ func (c *ClusterClient) cmdInfo(name string) *CommandInfo {
|
||||
}
|
||||
c.cmdsInfoOnce = &sync.Once{}
|
||||
})
|
||||
if c.cmdsInfo == nil {
|
||||
return nil
|
||||
}
|
||||
return c.cmdsInfo[name]
|
||||
}
|
||||
|
||||
func (c *ClusterClient) getNodes() map[string]*clusterNode {
|
||||
var nodes map[string]*clusterNode
|
||||
c.mu.RLock()
|
||||
if !c.closed {
|
||||
nodes = make(map[string]*clusterNode, len(c.nodes))
|
||||
c.mu.RLock()
|
||||
for addr, node := range c.nodes {
|
||||
nodes[addr] = node
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
return nodes
|
||||
}
|
||||
|
||||
@@ -257,18 +326,11 @@ func (c *ClusterClient) slotClosestNode(slot int) (*clusterNode, error) {
|
||||
|
||||
func (c *ClusterClient) cmdSlotAndNode(cmd Cmder) (int, *clusterNode, error) {
|
||||
cmdInfo := c.cmdInfo(cmd.arg(0))
|
||||
if cmdInfo == nil {
|
||||
internal.Logf("info for cmd=%s not found", cmd.arg(0))
|
||||
firstKey := cmd.arg(cmdFirstKeyPos(cmd, cmdInfo))
|
||||
if firstKey == "" {
|
||||
node, err := c.randomNode()
|
||||
return 0, node, err
|
||||
return -1, node, err
|
||||
}
|
||||
|
||||
if cmdInfo.FirstKeyPos == -1 {
|
||||
node, err := c.randomNode()
|
||||
return 0, node, err
|
||||
}
|
||||
|
||||
firstKey := cmd.arg(int(cmdInfo.FirstKeyPos))
|
||||
slot := hashtag.Slot(firstKey)
|
||||
|
||||
if cmdInfo.ReadOnly && c.opt.ReadOnly {
|
||||
@@ -330,9 +392,11 @@ func (c *ClusterClient) Process(cmd Cmder) error {
|
||||
var addr string
|
||||
moved, ask, addr = errors.IsMoved(err)
|
||||
if moved || ask {
|
||||
master, _ := c.slotMasterNode(slot)
|
||||
if moved && (master == nil || master.Client.getAddr() != addr) {
|
||||
c.lazyReloadSlots()
|
||||
if slot >= 0 {
|
||||
master, _ := c.slotMasterNode(slot)
|
||||
if moved && (master == nil || master.Client.getAddr() != addr) {
|
||||
c.lazyReloadSlots()
|
||||
}
|
||||
}
|
||||
|
||||
node, err = c.nodeByAddr(addr)
|
||||
@@ -609,69 +673,3 @@ func (c *ClusterClient) execClusterCmds(
|
||||
|
||||
return failedCmds, retErr
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// ClusterOptions are used to configure a cluster client and should be
|
||||
// passed to NewClusterClient.
|
||||
type ClusterOptions struct {
|
||||
// A seed list of host:port addresses of cluster nodes.
|
||||
Addrs []string
|
||||
|
||||
// The maximum number of retries before giving up. Command is retried
|
||||
// on network errors and MOVED/ASK redirects.
|
||||
// Default is 16.
|
||||
MaxRedirects int
|
||||
|
||||
// Enables read queries for a connection to a Redis Cluster slave node.
|
||||
ReadOnly bool
|
||||
|
||||
// Enables routing read-only queries to the closest master or slave node.
|
||||
RouteByLatency bool
|
||||
|
||||
// Following options are copied from Options struct.
|
||||
|
||||
Password string
|
||||
|
||||
DialTimeout time.Duration
|
||||
ReadTimeout time.Duration
|
||||
WriteTimeout time.Duration
|
||||
|
||||
// PoolSize applies per cluster node and not for the whole cluster.
|
||||
PoolSize int
|
||||
PoolTimeout time.Duration
|
||||
IdleTimeout time.Duration
|
||||
IdleCheckFrequency time.Duration
|
||||
}
|
||||
|
||||
func (opt *ClusterOptions) init() {
|
||||
if opt.MaxRedirects == -1 {
|
||||
opt.MaxRedirects = 0
|
||||
} else if opt.MaxRedirects == 0 {
|
||||
opt.MaxRedirects = 16
|
||||
}
|
||||
|
||||
if opt.RouteByLatency {
|
||||
opt.ReadOnly = true
|
||||
}
|
||||
}
|
||||
|
||||
func (opt *ClusterOptions) clientOptions() *Options {
|
||||
const disableIdleCheck = -1
|
||||
|
||||
return &Options{
|
||||
Password: opt.Password,
|
||||
ReadOnly: opt.ReadOnly,
|
||||
|
||||
DialTimeout: opt.DialTimeout,
|
||||
ReadTimeout: opt.ReadTimeout,
|
||||
WriteTimeout: opt.WriteTimeout,
|
||||
|
||||
PoolSize: opt.PoolSize,
|
||||
PoolTimeout: opt.PoolTimeout,
|
||||
IdleTimeout: opt.IdleTimeout,
|
||||
|
||||
// IdleCheckFrequency is not copied to disable reaper
|
||||
IdleCheckFrequency: disableIdleCheck,
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user