-
Notifications
You must be signed in to change notification settings - Fork 165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: replica selector #692
Changes from 4 commits
3185bbc
9499b9b
5261cb1
a7062fd
76f58a3
90387d2
9a7e752
91bbcf3
50fad71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,8 @@ import ( | |
var ErrNoSlot = errors.New("the slot has no redis node") | ||
var ErrReplicaOnlyConflict = errors.New("ReplicaOnly conflicts with SendToReplicas option") | ||
var ErrInvalidShardsRefreshInterval = errors.New("ShardsRefreshInterval must be greater than or equal to 0") | ||
var ErrReplicaOnlyConflictWithReaderNodeSelector = errors.New("ReplicaOnly conflicts with ReaderNodeSelector option") | ||
var ErrSendToReplicasConflictWithReaderNodeSelector = errors.New("SendToReplicas conflicts with ReaderNodeSelector option") | ||
|
||
type clusterClient struct { | ||
pslots [16384]conn | ||
|
@@ -41,6 +43,14 @@ type connrole struct { | |
replica bool | ||
} | ||
|
||
var sendToReader = func(cmd Completed) bool { | ||
return cmd.IsReadOnly() | ||
} | ||
|
||
var replicaOnlySelector = func(_ uint16, replicas []ReplicaInfo) int { | ||
return util.FastRand(len(replicas)) | ||
} | ||
|
||
func newClusterClient(opt *ClientOption, connFn connFn, retryer retryHandler) (*clusterClient, error) { | ||
client := &clusterClient{ | ||
cmd: cmds.NewBuilder(cmds.InitSlot), | ||
|
@@ -55,8 +65,20 @@ func newClusterClient(opt *ClientOption, connFn connFn, retryer retryHandler) (* | |
if opt.ReplicaOnly && opt.SendToReplicas != nil { | ||
return nil, ErrReplicaOnlyConflict | ||
} | ||
if opt.ReplicaOnly && opt.ReaderNodeSelector != nil { | ||
return nil, ErrReplicaOnlyConflictWithReaderNodeSelector | ||
} | ||
if opt.SendToReplicas != nil && opt.ReaderNodeSelector != nil { | ||
return nil, ErrSendToReplicasConflictWithReaderNodeSelector | ||
} | ||
|
||
if opt.SendToReplicas != nil { | ||
opt.ReaderNodeSelector = replicaOnlySelector | ||
} else if opt.ReaderNodeSelector != nil { | ||
opt.SendToReplicas = sendToReader | ||
} | ||
|
||
if opt.SendToReplicas != nil || opt.ReaderNodeSelector != nil { | ||
rOpt := *opt | ||
rOpt.ReplicaOnly = true | ||
client.rOpt = &rOpt | ||
|
@@ -236,15 +258,27 @@ func (c *clusterClient) _refresh() (err error) { | |
pslots[i] = conns[g.nodes[1+util.FastRand(nodesCount-1)]].conn | ||
} | ||
} | ||
case c.rOpt != nil: // implies c.opt.SendToReplicas != nil | ||
case c.rOpt != nil: | ||
if len(rslots) == 0 { // lazy init | ||
rslots = make([]conn, 16384) | ||
} | ||
if len(g.nodes) > 1 { | ||
n := len(g.nodes) - 1 | ||
replicas := make([]ReplicaInfo, 0, n) | ||
for _, addr := range g.nodes[1:] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we avoid this allocation by replacing g.nodes from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I already consider it. But i didn't. Because
Because of duplicated allocation, i also considering reusing I prefer reusing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @proost, Great considerations!
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I changed it. |
||
replicas = append(replicas, ReplicaInfo{Addr: addr}) | ||
} | ||
|
||
for _, slot := range g.slots { | ||
for i := slot[0]; i <= slot[1]; i++ { | ||
pslots[i] = conns[master].conn | ||
rslots[i] = conns[g.nodes[1+util.FastRand(len(g.nodes)-1)]].conn | ||
|
||
rIndex := c.opt.ReaderNodeSelector(uint16(i), replicas) | ||
if rIndex >= 0 && rIndex < n { | ||
rslots[i] = conns[g.nodes[1+rIndex]].conn | ||
} else { | ||
rslots[i] = conns[master].conn | ||
} | ||
} | ||
} | ||
} else { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel that we should not hide
SendToReplicas
when settingReaderNodeSelector
. That looks too implicit. Actually, I think we should ask users to provideSendToReplicas
whenReaderNodeSelector
is set:Instead of simply (ReaderNodeSelector alone is hard for me to reason what it actually does):
I prefer making it more explicitly state:
I believe the latter provides users with a better sense of control over which replicas to execute which commands.
Also, I think changing the name of ReaderNodeSelector to ReplicaSelector is more consistent with the existing naming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, Using
SendToReplicas
andReplicaSelector
both makes sense to me. Even though user can select primary usingReplicaSelector
, It is configured explicitly, so it is clear to me.90387d2