Skip to content
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

Merged
merged 13 commits into from
Dec 25, 2023
Merged

fix: send readonly command #430

merged 13 commits into from
Dec 25, 2023

Conversation

proost
Copy link
Contributor

@proost proost commented Dec 19, 2023

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?

@codecov-commenter
Copy link

codecov-commenter commented Dec 19, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (65f705a) 95.97% compared to head (87e2b39) 95.96%.
Report is 3 commits behind head on main.

❗ 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.
📢 Have feedback on the report? Share it here.

@rueian
Copy link
Collaborator

rueian commented Dec 19, 2023

Hi @proost, I think we should reuse this block:

rueidis/pipe.go

Lines 273 to 275 in 65f705a

if option.ReplicaOnly && option.Sentinel.MasterSet == "" {
init = append(init, []string{"READONLY"})
}

One more thing needs to be considered: A replica can be promoted to primary, meaning we should reset the 'READONLY' state with the READWRITE command.

@proost
Copy link
Contributor Author

proost commented Dec 22, 2023

@rueian
yes, you are right. replica can be promoted to primary. but it is hard to notice that replica is promoted.
I thinkg that adding ReplicaConnectionReadOnly to rueidis.ClientOption and sending "READONLY" command in _refresh can be better. because user knows when replica connection can be read-only connection. how do you think change like this?

@rueian
Copy link
Collaborator

rueian commented Dec 22, 2023

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.

@proost
Copy link
Contributor Author

proost commented Dec 23, 2023

@rueian

I change it. is okay to use ReplicaOnly? because it is breaking change.

cluster.go Outdated
Comment on lines 238 to 250
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)
Copy link
Collaborator

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.

Copy link
Collaborator

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

98040d7

@rueian
I'm sorry about serious misunderstanding. I figured out.

@proost proost requested a review from rueian December 23, 2023 13:27
cluster.go Outdated
conns[addr] = connrole{conn: c.connFn(addr, c.opt), replica: true}
var cc conn
if c.opt.ReplicaOnly {
opt := *c.opt
Copy link
Collaborator

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.

Copy link
Contributor Author

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"

Copy link
Collaborator

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cd6a674

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.

@proost proost requested a review from rueian December 23, 2023 13:58
cluster.go Outdated
Comment on lines 221 to 225
opt := *c.opt
if c.opt.SendToReplicas != nil {
opt.ReplicaOnly = false
}
conns[master] = connrole{conn: c.connFn(master, &opt), replica: false}
Copy link
Collaborator

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cd6a674

yes. you're right.

@proost proost requested a review from rueian December 24, 2023 08:51
cluster.go Outdated
Comment on lines 220 to 227
masterOpt := *c.opt
if c.opt.SendToReplicas != nil {
masterOpt.ReplicaOnly = false
}
replicaOpt := *c.opt
if c.opt.SendToReplicas != nil {
replicaOpt.ReplicaOnly = !c.opt.ReplicaOnly
}
Copy link
Collaborator

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.

Copy link
Collaborator

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 need c.replicaOpt in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5702ce3

Good! i changed it.

@proost proost requested a review from rueian December 24, 2023 13:54
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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ok && cc.replica == fresh.replica {
if ok && (cc.replica == fresh.replica || c.opt.SendToReplicas == nil) {

Copy link
Collaborator

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

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!

@proost proost requested a review from rueian December 25, 2023 12:44
@rueian rueian added the enhancement New feature or request label Dec 25, 2023
@rueian rueian merged commit b611f0c into redis:main Dec 25, 2023
@proost proost deleted the fix-readonly branch December 26, 2023 02:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants