-
Notifications
You must be signed in to change notification settings - Fork 183
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
fix: send readonly command #430
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## main #430 +/- ##
==========================================
- Coverage 95.97% 95.96% -0.02%
==========================================
Files 77 77
Lines 32403 32414 +11
==========================================
+ Hits 31099 31106 +7
- Misses 1110 1113 +3
- Partials 194 195 +1 ☔ View full report in Codecov by Sentry. |
Hi @proost, I think we should reuse this block: Lines 273 to 275 in 65f705a
One more thing needs to be considered: A replica can be promoted to primary, meaning we should reset the 'READONLY' state with the |
@rueian |
Hi @proost, I don’t think we need a new option. How about we modify the ReplicaOnly to true only for replica connections from the refresh function? And if we find a replica connection is promoted when refreshing, then we open a new one without ReplicaOnly. |
I change it. is okay to use |
cluster.go
Outdated
go func(cc conn) { | ||
defer wg.Done() | ||
|
||
timeout := c.opt.Dialer.Timeout | ||
if timeout <= 0 { | ||
timeout = DefaultDialTimeout | ||
} | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), timeout) | ||
defer cancel() | ||
|
||
cc.Do(ctx, cmds.NewCompleted([]string{"READONLY"})) // ignore error | ||
}(cc) |
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.
Hi @proost,
Thank you for the changes, but this goroutine looks wired to me and I don't expect there will be breaking change by reusing the ReplicaOnly
. I am afraid we have some misunderstanding here.
What I previously suggested is that the pipe
will send READONLY
command already if its ReplicaOnly
is true.
So I think we can reuse this behavior by modifying the c.connFn(addr, c.opt)
in the refresh function, and then the above goroutine is not necessary.
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.
Given that the signature of the connFn
is func(dst string, opt *ClientOption) conn
which accepts a ClientOption pointer, I think we can copy the c.opt
to c.optReplica
with the ReplicaOnly
set to true and then we can use c.connFn(addr, c.optReplica)
for replica connections.
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.
cluster.go
Outdated
conns[addr] = connrole{conn: c.connFn(addr, c.opt), replica: true} | ||
var cc conn | ||
if c.opt.ReplicaOnly { | ||
opt := *c.opt |
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 am still a little confused. So now, are we requiring users to set ReplicaOnly
as well even if they use SendToReplicas
?
I thought we would only set ReplicaOnly
transparently for users here to the opt
.
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.
fix it. 3ec3548
when SendToReplicas
is configured and ReplicaOnly
is true,
- connection to master: do not send "READONLY"
- connection to replica: send "READONLY"
when SendToReplicas
is configured and ReplicaOnly
is false,
- connection to master: do not send "READONLY"
- connection to replica: do not send "READONLY"
when SendToReplicas
is not configured and ReplicaOnly
is true,
- connection to master: send "READONLY"
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.
Why we don't send "READONLY" to replicas when SendToReplicas is configured and ReplicaOnly is false?
Isn't it the original issue you want to fix?
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.
Yes, it doesn't matter HOW. i just want to send "READONLY" command to replica connections automatically. but if you think read-only mode as a default is better than i changed it.
cluster.go
Outdated
opt := *c.opt | ||
if c.opt.SendToReplicas != nil { | ||
opt.ReplicaOnly = false | ||
} | ||
conns[master] = connrole{conn: c.connFn(master, &opt), replica: false} |
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.
The current code above will allocate a copy of c.opt
on the heap every time.
We should do it better by preparing the copied opt
once in advance.
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.
yes. you're right.
cluster.go
Outdated
masterOpt := *c.opt | ||
if c.opt.SendToReplicas != nil { | ||
masterOpt.ReplicaOnly = false | ||
} | ||
replicaOpt := *c.opt | ||
if c.opt.SendToReplicas != nil { | ||
replicaOpt.ReplicaOnly = !c.opt.ReplicaOnly | ||
} |
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.
It is better now. But it still allocates masterOpt
and replicaOpt
in every refresh.
We can make it even better by storing a c.replicaOpt
in the newClusterClient
.
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 just want to send "READONLY" command to replica connections automatically.
Agreed. So, how about this:
when SendToReplicas
is configured and ReplicaOnly
is true,
- return
ErrReplicaOnlyConflict
when SendToReplicas is configured and ReplicaOnly is false,
- connection to master: use
c.connFn(addr, c.opt)
- connection to replica: use
c.connFn(addr, c.replicaOpt)
when SendToReplicas is not configured and ReplicaOnly is true,
- connection to all nodes: use
c.connFn(addr, c.opt)
, we don't needc.replicaOpt
in this case.
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.
Good! i changed it.
cluster.go
Outdated
@@ -241,7 +254,8 @@ func (c *clusterClient) _refresh() (err error) { | |||
|
|||
c.mu.RLock() | |||
for addr, cc := range c.conns { | |||
if fresh, ok := conns[addr]; ok { | |||
fresh, ok := conns[addr] | |||
if ok && cc.replica == fresh.replica { |
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.
if ok && cc.replica == fresh.replica { | |
if ok && (cc.replica == fresh.replica || c.opt.SendToReplicas == nil) { |
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.
We can still reuse the conn if c.opt.SendToReplicas is not configured.
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.
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.
Hi @proost, thank you very much! It looks great!
I missed to send "READONLY" command for replica connection.
I want to integrate "READONLY" command with pipeline constructor, but it quite hard. has any idea?