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

change Backoff() algorithm #237

Merged
merged 1 commit into from
Mar 28, 2019
Merged

change Backoff() algorithm #237

merged 1 commit into from
Mar 28, 2019

Conversation

neganovalexey
Copy link
Contributor

  1. Add capabilities to handle Retry-After headers and similar info from
    server

Motivation: some servers provide Retry-After header or similar info along with 429
or 503 status code, and it is often important to honor such information
on retries, i.e. simple expotential backoff is not optimal.
https://docs.microsoft.com/en-us/sharepoint/dev/general-development/how-to-avoid-getting-throttled-or-blocked-in-sharepoint-online

  1. Add option NOT to retry even if operation returns an error (but retry
    by default, if no retry conditions are set)

Motivation: error are already passed to condition callback in resty, but
Backoff() still retries the request if error is not nil. It implies excessive,
stillborn retries for non-retryble errors from underlying http client
(i.e. with RoundTripper from oauth2).

  1. Remove error return value from condition callback

Motivation: this error is neither passed to caller, nor logged in any
way. It is cleaner to have "needRetry == true" than "needRetry == true
|| conditionErr != nil".

  1. Does not use floating-point arithmetics for expotential backoff

Motivation: simplification & performance

@neganovalexey
Copy link
Contributor Author

I will glad to receive any comments!
Maybe it will be nice to split the commit into several ones, but I think it is acceptable to show the draft in one piece.

@codecov
Copy link

codecov bot commented Mar 19, 2019

Codecov Report

Merging #237 into v2 will increase coverage by 0.09%.
The diff coverage is 95.45%.

Impacted file tree graph

@@            Coverage Diff             @@
##               v2     #237      +/-   ##
==========================================
+ Coverage   95.81%   95.91%   +0.09%     
==========================================
  Files           9        9              
  Lines        1076     1101      +25     
==========================================
+ Hits         1031     1056      +25     
  Misses         24       24              
  Partials       21       21
Impacted Files Coverage Δ
client.go 97.38% <100%> (+0.02%) ⬆️
request.go 100% <100%> (ø) ⬆️
retry.go 97.1% <95%> (+1.54%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46fc51a...fa429b9. Read the comment docs.

Copy link
Member

@jeevatkm jeevatkm left a comment

Choose a reason for hiding this comment

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

@neganovalexey Thank you for the PR.

I see a value in your explanations with motivation:

  • Add capabilities to handle Retry-After headers and similar info from the server
    • [Jeeva] Meaningful feature for the retry
  • Add option NOT to retry even if the operation returns an error (but retry by default, if no retry conditions are set)
    • [Jeeva] This is a kind of design choice, let's introduce this behavior and wait for user feedback to re-evaluate in the future on behavior.
  • Remove error return value from condition callback
    • [Jeeva] I like the simplification of return value to boolean, leaving error handling choice to the user

Curious to understand your reasoning -

  • Does not use floating-point arithmetics for exponential backoff
    • [Jeeva] Would you mind explaining it??

@jeevatkm
Copy link
Member

@neganovalexey Also, could you have a look at the test coverage? which edge case is missing from the unit test.

@pborzenkov
Copy link
Contributor

Add option NOT to retry even if the operation returns an error (but retry by default, if no retry conditions are set)
[Jeeva] This is a kind of design choice, let's introduce this behavior and wait for user feedback to re-evaluate in the future on behavior.

Unfortunately, if request is always retried in the presence of non-nil error, passing this err to retry condition callback doesn't make much sense, as a user can't disable retry for particular kinds of errors (e.g. invalid token, etc.).

@neganovalexey
Copy link
Contributor Author

neganovalexey commented Mar 21, 2019

@jeevatkm ,

@neganovalexey Also, could you have a look at the test coverage? which edge case is missing from the unit test.

done

@neganovalexey
Copy link
Contributor Author

@jeevatkm , floating-point arithmetics is less precise than integer arithmetics. It doesn't matter for this code, the corresponding changes were done mainly for aesthetic reasons (it is painful to see avoidable floating-point operations for a person with some C background).
I understand that it looks like some unnecessary refactoring and I'm going to return the old piece of code with math.Exp2() call.

1) Add capabilities to handle Retry-After headers and similar info from
server

Motivation: some servers provide Retry-After header or similar info along with 429
or 503 status code, and it is often important to honor such information
on retries, i.e. simple expotential backoff is not optimal.
https://docs.microsoft.com/en-us/sharepoint/dev/general-development/how-to-avoid-getting-throttled-or-blocked-in-sharepoint-online

2) Add option NOT to retry even if operation returns an error (but retry
by default, if no retry conditions are set)

Motivation: error are already passed to condition callback in resty, but
Backoff() still retries the request if error is not nil. It implies excessive,
stillborn retries for non-retryble errors from underlying http client
(i.e. with RoundTripper from oauth2).

3) Remove error return value from condition callback

Motivation: this error is neither passed to caller, nor logged in any
way. It is cleaner to have "needRetry == true" than "needRetry == true
|| conditionErr != nil".

4) Does not use floating-point arithmetics for expotential backoff

Motivation: simplification & performance
@jeevatkm
Copy link
Member

Unfortunately, if request is always retried in the presence of non-nil error, passing this err to retry condition callback doesn't make much sense, as a user can't disable retry for particular kinds of errors (e.g. invalid token, etc.).

@pborzenkov Its not always retried when error is non-nil. My understanding is - it happens only when retry count >0 and retry condition is not set.

@neganovalexey could you please validate my understanding and respond to @pborzenkov?

Copy link
Member

@jeevatkm jeevatkm left a comment

Choose a reason for hiding this comment

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

@neganovalexey Thank you for the PR 😄

@jeevatkm jeevatkm merged commit e5f377d into go-resty:v2 Mar 28, 2019
@pborzenkov
Copy link
Contributor

@jeevatkm I was referring to the original way the code was written. Merged patch definitely allows to stop retry for particular kinds of errors, which is good! Thanks!

@jeevatkm
Copy link
Member

@pborzenkov Thank you 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

3 participants