github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/xerrors/transport.go (about)

     1  package xerrors
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  
     8  	grpcCodes "google.golang.org/grpc/codes"
     9  	grpcStatus "google.golang.org/grpc/status"
    10  
    11  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/backoff"
    12  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
    13  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
    14  )
    15  
    16  type transportError struct {
    17  	status  *grpcStatus.Status
    18  	err     error
    19  	address string
    20  	nodeID  uint32
    21  	traceID string
    22  }
    23  
    24  func (e *transportError) GRPCStatus() *grpcStatus.Status {
    25  	return e.status
    26  }
    27  
    28  func (e *transportError) isYdbError() {}
    29  
    30  func (e *transportError) Code() int32 {
    31  	return int32(e.status.Code())
    32  }
    33  
    34  func (e *transportError) Name() string {
    35  	return "transport/" + e.status.Code().String()
    36  }
    37  
    38  type teOpt interface {
    39  	applyToTransportError(te *transportError)
    40  }
    41  
    42  type addressOption string
    43  
    44  func (address addressOption) applyToTransportError(te *transportError) {
    45  	te.address = string(address)
    46  }
    47  
    48  func WithAddress(address string) addressOption {
    49  	return addressOption(address)
    50  }
    51  
    52  type nodeIDOption uint32
    53  
    54  func (nodeID nodeIDOption) applyToTransportError(te *transportError) {
    55  	te.nodeID = uint32(nodeID)
    56  }
    57  
    58  func WithNodeID(nodeID uint32) nodeIDOption {
    59  	return nodeIDOption(nodeID)
    60  }
    61  
    62  func (e *transportError) Error() string {
    63  	b := xstring.Buffer()
    64  	defer b.Free()
    65  	b.WriteString(e.Name())
    66  	fmt.Fprintf(b, " (code = %d, source error = %q", e.status.Code(), e.err.Error())
    67  	if len(e.address) > 0 {
    68  		fmt.Fprintf(b, ", address: %q", e.address)
    69  	}
    70  	if e.nodeID > 0 {
    71  		b.WriteString(", nodeID = ")
    72  		b.WriteString(strconv.FormatUint(uint64(e.nodeID), 10))
    73  	}
    74  	if len(e.traceID) > 0 {
    75  		fmt.Fprintf(b, ", traceID: %q", e.traceID)
    76  	}
    77  	b.WriteString(")")
    78  
    79  	return b.String()
    80  }
    81  
    82  func (e *transportError) Unwrap() error {
    83  	return e.err
    84  }
    85  
    86  func (e *transportError) Type() Type {
    87  	switch e.status.Code() {
    88  	case
    89  		grpcCodes.Aborted,
    90  		grpcCodes.ResourceExhausted:
    91  		return TypeRetryable
    92  	case
    93  		grpcCodes.Internal,
    94  		grpcCodes.Canceled,
    95  		grpcCodes.DeadlineExceeded,
    96  		grpcCodes.Unavailable:
    97  		return TypeConditionallyRetryable
    98  	default:
    99  		return TypeUndefined
   100  	}
   101  }
   102  
   103  func (e *transportError) BackoffType() backoff.Type {
   104  	switch e.status.Code() {
   105  	case
   106  		grpcCodes.Internal,
   107  		grpcCodes.Canceled,
   108  		grpcCodes.DeadlineExceeded,
   109  		grpcCodes.Unavailable:
   110  		return backoff.TypeFast
   111  	case grpcCodes.ResourceExhausted:
   112  		return backoff.TypeSlow
   113  	default:
   114  		return backoff.TypeNoBackoff
   115  	}
   116  }
   117  
   118  func (e *transportError) IsRetryObjectValid() bool {
   119  	switch e.status.Code() {
   120  	case
   121  		grpcCodes.ResourceExhausted,
   122  		grpcCodes.OutOfRange:
   123  		return true
   124  	default:
   125  		return false
   126  	}
   127  }
   128  
   129  // IsTransportError reports whether err is transportError with given grpc codes
   130  func IsTransportError(err error, codes ...grpcCodes.Code) bool {
   131  	if err == nil {
   132  		return false
   133  	}
   134  	var status *grpcStatus.Status
   135  	if t := (*transportError)(nil); errors.As(err, &t) {
   136  		status = t.status
   137  	} else if t, has := grpcStatus.FromError(err); has {
   138  		status = t
   139  	}
   140  	if status != nil {
   141  		if len(codes) == 0 {
   142  			return true
   143  		}
   144  		for _, code := range codes {
   145  			if status.Code() == code {
   146  				return true
   147  			}
   148  		}
   149  	}
   150  
   151  	return false
   152  }
   153  
   154  // Transport returns a new transport error with given options
   155  func Transport(err error, opts ...teOpt) error {
   156  	if err == nil {
   157  		return nil
   158  	}
   159  	var te *transportError
   160  	if errors.As(err, &te) {
   161  		return te
   162  	}
   163  	if s, ok := grpcStatus.FromError(err); ok {
   164  		te = &transportError{
   165  			status: s,
   166  			err:    err,
   167  		}
   168  	} else {
   169  		te = &transportError{
   170  			status: grpcStatus.New(grpcCodes.Unknown, stack.Record(1)),
   171  			err:    err,
   172  		}
   173  	}
   174  	for _, opt := range opts {
   175  		if opt != nil {
   176  			opt.applyToTransportError(te)
   177  		}
   178  	}
   179  
   180  	return te
   181  }
   182  
   183  func TransportError(err error) Error {
   184  	if err == nil {
   185  		return nil
   186  	}
   187  	var t *transportError
   188  	if errors.As(err, &t) {
   189  		return t
   190  	}
   191  	if s, ok := grpcStatus.FromError(err); ok {
   192  		return &transportError{
   193  			status: s,
   194  			err:    err,
   195  		}
   196  	}
   197  
   198  	return nil
   199  }