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

Rewrite util/retry as a for-loop #1396

Merged
merged 4 commits into from
Jun 29, 2015
Merged

Rewrite util/retry as a for-loop #1396

merged 4 commits into from
Jun 29, 2015

Conversation

tamird
Copy link
Contributor

@tamird tamird commented Jun 16, 2015

The final proposal! PTAL.

@tbg tbg added the PTAL label Jun 16, 2015
if err := gogoproto.Unmarshal(b, call.Reply); err != nil {
return retry.Continue, err
if err = gogoproto.Unmarshal(b, call.Reply); err != nil {
continue
Copy link
Member

Choose a reason for hiding this comment

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

the error should be logged.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@tbg
Copy link
Member

tbg commented Jun 17, 2015

I like this approach, though the diff also showcases that it's much less elegant when the retryable code has a lot of return statements (which would prompt wrapping it in a function again, thus making the current solution the elegant one). But we can easily have both if we add just one small convenience method to retry which takes a func(Resettable) err where Resettable is nothing but Reset(), and functions can return a struct retry.AbortError{error} to indicate that they've run into something nasty to abort the loop (which then returns the wrapper error to the caller).
That would allow us to take whichever fits the situation best and with that addition I opt for going down the route presented in this PR.

}

defer resp.Body.Close()
Copy link
Contributor

Choose a reason for hiding this comment

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

Defers work at function scope, not block scope. With this pattern response bodies won't get closed until the last retry has completed. (and in go's http client, failing to close a response body prevents that connection from being reused). When a retry loop contains a defer, I think we need to refactor the body into a separate function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in go's http client, failing to close a response body prevents that connection from being reused

Ah, I didn't know this (and was otherwise fine with closing at the very end of the function). I'll fix this up.

@petermattis
Copy link
Collaborator

I didn't look at the implementation in depth, but the for-loop API looks good to me.

@tamird tamird closed this Jun 22, 2015
@tamird tamird deleted the retry-for-loop branch June 22, 2015 15:57
@tamird tamird restored the retry-for-loop branch June 22, 2015 16:41
@tamird tamird reopened this Jun 22, 2015
@tamird
Copy link
Contributor Author

tamird commented Jun 22, 2015

But we can easily have both if we add just one small convenience method to retry which takes a func(Resettable) err where Resettable is nothing but Reset(), and functions can return a struct retry.AbortError{error} to indicate that they've run into something nasty to abort the loop (which then returns the wrapper error to the caller).

This sounds nice, but I think providing this choice is a mistake. It's just not the Go way; if a situation calls for this, this facility can trivially be written inline, and the sugar will just serve to obfuscate in this case.

@tbg
Copy link
Member

tbg commented Jun 23, 2015

Not sure the "Go way" mandates repeating yourself without a good reason, and it's a bit much to inline. Let's just see how far you get, no need to add it prematurely.

@tamird
Copy link
Contributor Author

tamird commented Jun 29, 2015

OK, this is finally complete. PTAL!

@tamird tamird changed the title Retry as for-loop: POC Rewrite util/retry as a for-loop Jun 29, 2015
InitialBackoff: 1 * time.Millisecond,
MaxBackoff: 5 * time.Millisecond,
Multiplier: 2,
MaxRetries: 2,
Copy link
Member

Choose a reason for hiding this comment

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

does MaxRetries=2 correspond to MaxAttempts=3?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gah, no, it's off by one

@petermattis
Copy link
Collaborator

LGTM

@tamird
Copy link
Contributor Author

tamird commented Jun 29, 2015

Pushed some commits to address all the feedback. I'll squash and merge when this is green. @spencerkimball mind taking a look?


// Retry implements the public methods necessary to control an exponential-
// backoff retry loop.
type Retry struct {
Copy link
Member

Choose a reason for hiding this comment

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

It would be helpful to have a usage example in comments.

tamird added a commit that referenced this pull request Jun 29, 2015
Rewrite util/retry as a for-loop
@tamird tamird merged commit 830e67a into cockroachdb:master Jun 29, 2015
@tamird tamird deleted the retry-for-loop branch June 29, 2015 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants