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 }