Skip to content

Commit f4ac2d9

Browse files
committed
merge: No delay after final retry on max attempts avast#129
1 parent cbf4dbf commit f4ac2d9

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

retry.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ func DoWithData[T any](retryableFunc RetryableFuncWithData[T], opts ...Option) (
175175
attemptsForError[err] = attempts
176176
}
177177

178-
shouldRetry := true
179-
for shouldRetry {
178+
shouldRetry:
179+
for {
180180
t, err := retryableFunc()
181181
if err == nil {
182182
return t, nil
@@ -194,13 +194,15 @@ func DoWithData[T any](retryableFunc RetryableFuncWithData[T], opts ...Option) (
194194
if errors.Is(err, errToCheck) {
195195
attempts--
196196
attemptsForError[errToCheck] = attempts
197-
shouldRetry = shouldRetry && attempts > 0
197+
if attempts <= 0 {
198+
break shouldRetry
199+
}
198200
}
199201
}
200202

201203
// if this is last attempt - don't wait
202204
if n == config.attempts-1 {
203-
break
205+
break shouldRetry
204206
}
205207
n++
206208
select {
@@ -212,8 +214,6 @@ func DoWithData[T any](retryableFunc RetryableFuncWithData[T], opts ...Option) (
212214

213215
return emptyT, append(errorLog, context.Cause(config.context))
214216
}
215-
216-
shouldRetry = shouldRetry && n < config.attempts
217217
}
218218

219219
if config.lastErrorOnly {

retry_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,52 @@ func BenchmarkDoWithDataNoErrors(b *testing.B) {
632632
}
633633
}
634634

635+
type attemptsForErrorTestError struct{}
636+
637+
func (attemptsForErrorTestError) Error() string { return "test error" }
638+
639+
func TestAttemptsForErrorNoDelayAfterFinalAttempt(t *testing.T) {
640+
var count uint64
641+
var timestamps []time.Time
642+
643+
startTime := time.Now()
644+
645+
err := Do(
646+
func() error {
647+
count++
648+
timestamps = append(timestamps, time.Now())
649+
return attemptsForErrorTestError{}
650+
},
651+
Attempts(3),
652+
Delay(200*time.Millisecond),
653+
DelayType(FixedDelay),
654+
AttemptsForError(2, attemptsForErrorTestError{}),
655+
LastErrorOnly(true),
656+
Context(context.Background()),
657+
)
658+
659+
endTime := time.Now()
660+
661+
assert.Error(t, err)
662+
assert.Equal(t, uint64(2), count, "should attempt exactly 2 times")
663+
assert.Len(t, timestamps, 2, "should have 2 timestamps")
664+
665+
// Verify timing: first attempt at ~0ms, second at ~200ms, end immediately after second attempt
666+
firstAttemptTime := timestamps[0].Sub(startTime)
667+
secondAttemptTime := timestamps[1].Sub(startTime)
668+
totalTime := endTime.Sub(startTime)
669+
670+
// First attempt should be immediate
671+
assert.Less(t, firstAttemptTime, 50*time.Millisecond, "first attempt should be immediate")
672+
673+
// Second attempt should be after delay
674+
assert.Greater(t, secondAttemptTime, 150*time.Millisecond, "second attempt should be after delay")
675+
assert.Less(t, secondAttemptTime, 250*time.Millisecond, "second attempt should not be too delayed")
676+
677+
// Total time should not include delay after final attempt
678+
assert.Less(t, totalTime, 300*time.Millisecond, "should not delay after final attempt")
679+
}
680+
635681
func TestIsRecoverable(t *testing.T) {
636682
err := errors.New("err")
637683
assert.True(t, IsRecoverable(err))

0 commit comments

Comments
 (0)