-
Notifications
You must be signed in to change notification settings - Fork 271
feat: add ExponentialJitterBackoff
backoff strategy
#256
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
base: main
Are you sure you want to change the base?
Conversation
func ExponentialJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { | ||
baseBackoff := DefaultBackoff(min, max, attemptNum, resp) | ||
|
||
if resp != 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.
Question: Default backoff already handles Retry-After
. Is this check required again? ref
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.
This check is needed to avoid adding the jitter when the Retry-After
header has been handled, that is returning the backoff returned by DefaultBackoff
as it is.
} | ||
|
||
// Seed randomization; it's OK to do it every time | ||
rnd := rand.New(rand.NewSource(time.Now().UnixNano())) |
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.
Suggestion: To maintain consistency, can we use the same source like here?
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 can factor out a new function to get the randomization source. Is that what you mean?
jitter := rnd.Float64()*0.5 - 0.25 // Random value between -0.25 e +0.25 | ||
jitteredSleep := time.Duration(float64(baseBackoff) * (1.0 + jitter)) | ||
|
||
return clampDuration(jitteredSleep, min, max) |
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.
Question: Why is the jitter between -0.25 and +0.25 and not between min
and max
like in LinearJitterBackoff?
Also, since min
and max
mean different things between the two functions, it would be worthwhile adding it to the function documentation.
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.
Question: Why is the jitter between -0.25 and +0.25 and not between min and max like in LinearJitterBackoff?
As far as I can see, the min
and max
arguments should be respectively treated as absolute lower and
upper limits for the computed backoff. In that regard, the LinearJitterBackoff
function is the only one that treats them slightly differently. Quoting its documentation:
min and max here are not absolute values. The number to be multiplied by
the attempt number will be chosen at random from between them, thus they are
bounding the jitter.
Said that, to guarantee the exponential trend of the backoff handled by DefaultBackoff
, I think the jitter should necessarily be within a "neighborhood", in a mathematical sense, of the value returned by DefaultBackoff
. Hence, a random value is picked between the interval [ backoff - 0.25 * backoff, backoff + 0.25 * backoff ]
.
Regarding your other observation, instead:
Also, since min and max mean different things between the two functions, it would be worthwhile adding it to the function documentation.
I'll improve the documentation of the new function to explain how the arguments are used.
func clampDuration(d, min, max time.Duration) time.Duration { | ||
if d < min { | ||
return min | ||
} | ||
if d > max { | ||
return max | ||
} | ||
return d | ||
} | ||
|
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.
Nit: This function could've been inlined.
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 reason for this function to exist is to fully test it in isolation.
As this clamping is performed for a random value, there's no guarantee of full test coverage.
@abhijeetviswa Sorry for the noise in the unit tests. Fixing those as well. |
The new strategy is an extension of the default one that applies a jitter to avoid thundering herd.
The new strategy is an extension of the default one that applies a jitter to avoid thundering herd.