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
Small refactor cancel context #11
Small refactor cancel context #11
Conversation
@@ -73,13 +73,6 @@ func Do(ctx context.Context, b Backoff, f RetryFunc) error { | |||
return rerr.Unwrap() | |||
} | |||
|
|||
// ctx.Done() has priority, so we test it alone first | |||
select { |
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.
Sadly, I think we actually need both. I think we need to keep this block and add it at the start of the for
.
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.
/cc @Deleplace
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.
It's interesting because it's subtle and hard to reason about, even for such a relatively small amount of code.
The code on the left (before PR #11) checks ctx.Done()
:
- once before the loop, in case
ctx
was canceled before the 1st call tof
- once alone after f, in case
ctx
was canceled during the call tof
(which can be slow) - once in the delay step, in case
ctx
is canceled during the backoff pause
It is my understanding that ctx.Done()
is already checked just before executing f
, in all cases. At the end of the loop, the case where time.After
"wins" is sufficient to proceed with looping and executing f
.
Thus, I suggest to leave retry.go
unchanged.
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 think the behavior is still the same (please correct me if there's any special case). I'll explain each case:
once before the loop, in case ctx was canceled before the 1st call to f
- The check now is put before calling
f
in the loop so it will make sure thatf
will never execute if context was canceled
once alone after f, in case ctx was canceled during the call to f (which can be slow)
- It will be checked by the
select
block at the end of the loop
once in the delay step, in case ctx is canceled during the backoff pause
- This will also be checked by that
select
block. Here we have 3 cases:- If the context was canceled before, then
ctx.Done()
case is executed and the retry will stop - If the context is not canceled and the delay time is over then it will continue the loop --> check the context again (at the beginning of the loop)--> still not canceled --> execute
f
- In case there's a race between two conditions and
time.After
wins (because it's randomly chosen) then it will continue the loop --> check the context --> context is canceled --> return immediately
- If the context was canceled before, then
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 do agree that the behaviour is the same.
So I'm changing my mind: in the light that this change in retry.go is not a bugfix, but a code style improvement, I think the change is good and may be merged.
This pull request has been automatically locked since there has not |
Description
f
so if the context is canceled and it's not selected at the end of the loop, it will be checked one more time herebenchmark_test
to use the latest version of go-retry