errors: add IsRetrayable func #273

Merged
vtolstov merged 2 commits from errors into v3 2023-10-25 10:24:59 +03:00
Showing only changes of commit df4f96a2d8 - Show all commits

View File

@ -4,11 +4,17 @@ package errors // import "go.unistack.org/micro/v3/errors"
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
) )
var ( var (
@ -340,3 +346,101 @@ func addslashes(str string) string {
} }
return buf.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
}