client: determenistic retry backoff

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2022-03-27 00:16:22 +03:00
parent 35ab6ae84e
commit 680ac11ef9
4 changed files with 36 additions and 7 deletions

View File

@ -2,6 +2,7 @@ package client
import (
"context"
"math"
"time"
"go.unistack.org/micro/v3/util/backoff"
@ -10,6 +11,20 @@ import (
// BackoffFunc is the backoff call func
type BackoffFunc func(ctx context.Context, req Request, attempts int) (time.Duration, error)
func exponentialBackoff(ctx context.Context, req Request, attempts int) (time.Duration, error) {
// BackoffExp using exponential backoff func
func BackoffExp(_ context.Context, _ Request, attempts int) (time.Duration, error) {
return backoff.Do(attempts), nil
}
// BackoffInterval specifies randomization interval for backoff func
func BackoffInterval(min time.Duration, max time.Duration) BackoffFunc {
return func(_ context.Context, _ Request, attempts int) (time.Duration, error) {
td := time.Duration(time.Duration(math.Pow(float64(attempts), math.E)) * time.Millisecond * 100)
if td < min {
return min, nil
} else if td > max {
return max, nil
}
return td, nil
}
}

View File

@ -22,7 +22,7 @@ func TestBackoff(t *testing.T) {
}
for i := 0; i < 5; i++ {
d, err := exponentialBackoff(context.TODO(), r, i)
d, err := BackoffExp(context.TODO(), r, i)
if err != nil {
t.Fatal(err)
}

View File

@ -14,8 +14,8 @@ var (
DefaultClient Client = NewClient()
// DefaultContentType is the default content-type if not specified
DefaultContentType = "application/json"
// DefaultBackoff is the default backoff function for retries
DefaultBackoff = exponentialBackoff
// DefaultBackoff is the default backoff function for retries (minimum 10 millisecond and maximum 5 second)
DefaultBackoff = BackoffInterval(10*time.Millisecond, 5*time.Second)
// DefaultRetry is the default check-for-retry function for retries
DefaultRetry = RetryNever
// DefaultRetries is the default number of times a request is tried

View File

@ -19,18 +19,32 @@ func RetryNever(ctx context.Context, req Request, retryCount int, err error) (bo
return false, nil
}
// RetryOnError retries a request on a 500 or timeout error
// RetryOnError retries a request on a 500 or 408 (timeout) error
func RetryOnError(_ context.Context, _ Request, _ int, err error) (bool, error) {
if err == nil {
return false, nil
}
me := errors.FromError(err)
switch me.Code {
// retry on timeout or internal server error
case 408, 500:
return true, nil
}
return false, nil
}
// RetryOnErrors retries a request on specified error codes
func RetryOnErrors(codes ...int32) RetryFunc {
return func(_ context.Context, _ Request, _ int, err error) (bool, error) {
if err == nil {
return false, nil
}
me := errors.FromError(err)
for _, code := range codes {
if me.Code == code {
return true, nil
}
}
return false, nil
}
}