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

Feature: retry backoffs #633

Open
rueian opened this issue Sep 13, 2024 · 6 comments · May be fixed by #643
Open

Feature: retry backoffs #633

rueian opened this issue Sep 13, 2024 · 6 comments · May be fixed by #643
Labels
enhancement New feature or request feature good first issue Good for newcomers

Comments

@rueian
Copy link
Collaborator

rueian commented Sep 13, 2024

There are about 25 goto retry statements in rueidis. Such as:

rueidis/cluster.go

Lines 460 to 462 in 5135acd

runtime.Gosched()
goto retry
}

rueidis/client.go

Lines 48 to 50 in 5135acd

if c.retry && cmd.IsReadOnly() && c.isRetryable(resp.NonRedisError(), ctx) {
goto retry
}

rueidis/sentinel.go

Lines 66 to 68 in 5135acd

if c.retry && cmd.IsReadOnly() && c.isRetryable(resp.NonRedisError(), ctx) {
goto retry
}

And so on.

It will be better to add backoffs before ALL these statements to avoid rapid retry storms and those backoffs should have the following properties:

  1. Should be capped for at most 1 second.
  2. Should not exceed the context deadline.
  3. Backoff exponentially with jitter.

Related to valkey-io/valkey-doc#164

@rueian rueian added enhancement New feature or request good first issue Good for newcomers feature labels Sep 13, 2024
@proost
Copy link
Contributor

proost commented Sep 26, 2024

@rueian It's good idea.

Maybe later someone want own retry logic. So default retry logic make as you said, but how about make a retrier function like this?

// Retrier is a function that will be called when a command is retryable.
// The function should return an error if the command should not be retried.
type Retrier func(ctx context.Context, attempts int) error 

@rueian
Copy link
Collaborator Author

rueian commented Sep 26, 2024

@proost sounds good, but the returned 'error' makes the Retrier confusing in terms of its responsibility.

How about just return a time.Duration and name the function RetryDelay?

@proost
Copy link
Contributor

proost commented Sep 27, 2024

@rueian Ok, I understand what's your intention. So Let's clear interface.

How do you think?

// RetryDelay returns the delay that should be used before retrying the
// attempt. Will return error if the delay could not be determined or do not retry.
type RetryDelay func(attempts int, err error) (time.Duration, error)

If you agree with definition, I will make a feature.

@rueian
Copy link
Collaborator Author

rueian commented Sep 27, 2024

Returning an error still seems confusing to me. We can use negative duration to stop retrying. I think returning a duration only is much more clear.

One more thing. If the return duration exceeds the context deadline, we should not retry. So there might be some duplications of checking the context deadline in the old isRetryable function and the new RetryDelay that we had better keep in one place.

@proost
Copy link
Contributor

proost commented Sep 28, 2024

@rueian how about this?

// RetryDelay returns the delay that should be used before retrying the
// attempt. Will return negative delay if the delay could not be determined or do not retry.
type RetryDelay func(attempts int, err error) time.Duration

One more thing. If the return duration exceeds the context deadline, we should not retry. So there might be some duplications of checking the context deadline in the old isRetryable function and the new RetryDelay that we had better keep in one place.

I considered it already. so I plan like this:

  1. call old isRetryable function
  2. if retryable, call RetryDelay
  3. wait until delay time or context is done.

@rueian
Copy link
Collaborator Author

rueian commented Sep 28, 2024

Yes, looks good to me.

@proost proost linked a pull request Oct 3, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feature good first issue Good for newcomers
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants