client: determenistic retry backoff #107
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user