errors: add IsRetrayable func
lint / lint (pull_request) Successful in 1m18s Details
pr / test (pull_request) Successful in 1m3s Details

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2023-10-23 02:35:10 +03:00
parent fac3b20bd4
commit df4f96a2d8
1 changed files with 104 additions and 0 deletions

View File

@ -4,11 +4,17 @@ package errors // import "go.unistack.org/micro/v3/errors"
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var (
@ -340,3 +346,101 @@ func addslashes(str string) string {
}
return buf.String()
}
type retryableError struct {
err error
}
// Retryable returns error that can be retried later
func Retryable(err error) error {
return &retryableError{err: err}
}
// Unwrap provides error wrapping
func (e *retryableError) Unwrap() error {
return e.err
}
// Error returns the error string
func (e *retryableError) Error() string {
if e.err == nil {
return ""
}
return e.err.Error()
}
// IsRetryable checks error for ability to retry later
func IsRetryable(err error) bool {
switch verr := err.(type) {
case *Error:
switch verr.Code {
case 401, 403, 408, 500, 501, 502, 503, 504:
return true
default:
return false
}
case *retryableError:
return true
case interface{ SafeToRetry() bool }:
return verr.SafeToRetry()
case interface{ Timeout() bool }:
return verr.Timeout()
}
switch {
case errors.Is(err, io.EOF), errors.Is(err, io.ErrUnexpectedEOF):
return true
case errors.Is(err, context.DeadlineExceeded):
return true
case errors.Is(err, io.ErrClosedPipe), errors.Is(err, io.ErrShortBuffer), errors.Is(err, io.ErrShortWrite):
return true
default:
st, ok := status.FromError(err)
if !ok {
errmsg := err.Error()
if strings.Contains(errmsg, `number of field descriptions must equal number of`) {
return true
}
if strings.Contains(errmsg, `not a pointer`) {
return true
}
if strings.Contains(errmsg, `values, but dst struct has only`) {
return true
}
if strings.Contains(errmsg, `struct doesn't have corresponding row field`) {
return true
}
if strings.Contains(errmsg, `cannot find field`) {
return true
}
if strings.Contains(errmsg, `cannot scan`) || strings.Contains(errmsg, `cannot convert`) {
return true
}
if strings.Contains(errmsg, `failed to connect to`) {
return true
}
return false
}
switch st.Code() {
case codes.Unavailable, codes.ResourceExhausted:
return true
case codes.DeadlineExceeded:
return true
case codes.Internal:
if strings.Contains(st.Message(), `transport: received the unexpected content-type "text/html; charset=UTF-8"`) {
return true
}
if strings.Contains(st.Message(), io.ErrUnexpectedEOF.Error()) {
return true
}
if strings.Contains(st.Message(), `stream terminated by RST_STREAM with error code: INTERNAL_ERROR`) {
return true
}
default:
return false
}
}
return false
}