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