Skip to content

Commit

Permalink
Refine the retryable checker
Browse files Browse the repository at this point in the history
Signed-off-by: JmPotato <[email protected]>
  • Loading branch information
JmPotato committed May 30, 2024
1 parent 3b42fc6 commit d952e06
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 16 deletions.
4 changes: 2 additions & 2 deletions client/http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,11 @@ func (ci *clientInner) requestWithRetry(
}
// Copy a new backoffer for each request.
bo := *reqInfo.bo
// Set the retryable checker for the backoffer.
// Set the retryable checker for the backoffer if it's not set.
bo.SetRetryableChecker(func(err error) bool {
// Backoffer also needs to check the status code to determine whether to retry.
return err != nil && !noNeedRetry(statusCode)
})
}, false)
return bo.Exec(ctx, execFunc)
}

Expand Down
24 changes: 14 additions & 10 deletions client/retry/backoff.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type Backoffer struct {
// total defines the max total time duration cost in retrying. If it's 0, it means infinite retry until success.
total time.Duration
// retryableChecker is used to check if the error is retryable.
// By default, all errors are retryable.
// If it's not set, it will use `defaultRetryableChecker` to retry on all non-nil errors.
retryableChecker func(err error) bool
// logInterval defines the log interval for retrying.
logInterval time.Duration
Expand Down Expand Up @@ -132,12 +132,9 @@ func InitialBackoffer(base, max, total time.Duration, opts ...Option) *Backoffer
total = base
}
bo := &Backoffer{
base: base,
max: max,
total: total,
retryableChecker: func(err error) bool {
return err != nil
},
base: base,
max: max,
total: total,
next: base,
currentTotal: 0,
attempt: 0,
Expand All @@ -148,18 +145,25 @@ func InitialBackoffer(base, max, total time.Duration, opts ...Option) *Backoffer
return bo
}

// SetRetryableChecker sets the retryable checker.
func (bo *Backoffer) SetRetryableChecker(checker func(err error) bool) {
// SetRetryableChecker sets the retryable checker, `overwrite` flag is used to indicate whether to overwrite the existing checker.
func (bo *Backoffer) SetRetryableChecker(checker func(err error) bool, overwrite bool) {
if !overwrite && bo.retryableChecker != nil {
return
}
bo.retryableChecker = checker
}

func (bo *Backoffer) isRetryable(err error) bool {
if bo.retryableChecker == nil {
return true
return defaultRetryableChecker(err)
}
return bo.retryableChecker(err)
}

func defaultRetryableChecker(err error) bool {
return err != nil
}

// nextInterval for now use the `exponentialInterval`.
func (bo *Backoffer) nextInterval() time.Duration {
return bo.exponentialInterval()
Expand Down
26 changes: 22 additions & 4 deletions client/retry/backoff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,34 @@ func TestBackoffer(t *testing.T) {
// Test the retryable checker.
execCount = 0
bo = InitialBackoffer(base, max, total)
bo.SetRetryableChecker(func(error) bool {
retryableChecker := func(error) bool {
return execCount < 2
})
err = bo.Exec(ctx, func() error {
}
bo.SetRetryableChecker(retryableChecker, false)
execFunc := func() error {
execCount++
return nil
})
}
err = bo.Exec(ctx, execFunc)
re.NoError(err)
re.Equal(2, execCount)
re.True(isBackofferReset(bo))
// Test the retryable checker with overwrite.
execCount = 0
retryableChecker = func(error) bool {
return execCount < 4
}
bo.SetRetryableChecker(retryableChecker, false)
err = bo.Exec(ctx, execFunc)
re.NoError(err)
re.Equal(2, execCount)
re.True(isBackofferReset(bo))
execCount = 0
bo.SetRetryableChecker(retryableChecker, true)
err = bo.Exec(ctx, execFunc)
re.NoError(err)
re.Equal(4, execCount)
re.True(isBackofferReset(bo))
}

func isBackofferReset(bo *Backoffer) bool {
Expand Down

0 comments on commit d952e06

Please sign in to comment.