github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/errors/helper.go (about) 1 // Copyright 2020 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package errors 15 16 import ( 17 "context" 18 "net/http" 19 "strings" 20 21 "github.com/pingcap/errors" 22 "google.golang.org/grpc/codes" 23 "google.golang.org/grpc/status" 24 ) 25 26 // WrapError generates a new error based on given `*errors.Error`, wraps the err 27 // as cause error. 28 // If given `err` is nil, returns a nil error, which a the different behavior 29 // against `Wrap` function in pingcap/errors. 30 func WrapError(rfcError *errors.Error, err error, args ...interface{}) error { 31 if err == nil { 32 return nil 33 } 34 return rfcError.Wrap(err).GenWithStackByArgs(args...) 35 } 36 37 // ChangeFeedGCFastFailError is read only. 38 // If this type of error occurs in a changefeed, it means that the data it 39 // wants to replicate has been or will be GC. So it makes no sense to try to 40 // resume the changefeed, and the changefeed should immediately be failed. 41 var ChangeFeedGCFastFailError = []*errors.Error{ 42 ErrGCTTLExceeded, ErrSnapshotLostByGC, ErrStartTsBeforeGC, 43 } 44 45 // IsChangefeedGCFastFailError checks if an error is a ChangefeedFastFailError 46 func IsChangefeedGCFastFailError(err error) bool { 47 if err == nil { 48 return false 49 } 50 for _, e := range ChangeFeedGCFastFailError { 51 if e.Equal(err) { 52 return true 53 } 54 rfcCode, ok := RFCCode(err) 55 if ok && e.RFCCode() == rfcCode { 56 return true 57 } 58 } 59 return false 60 } 61 62 // IsChangefeedGCFastFailErrorCode checks the error code, returns true if it is a 63 // ChangefeedFastFailError code 64 func IsChangefeedGCFastFailErrorCode(errCode errors.RFCErrorCode) bool { 65 for _, e := range ChangeFeedGCFastFailError { 66 if errCode == e.RFCCode() { 67 return true 68 } 69 } 70 return false 71 } 72 73 var changefeedUnRetryableErrors = []*errors.Error{ 74 ErrExpressionColumnNotFound, 75 ErrExpressionParseFailed, 76 ErrSchemaSnapshotNotFound, 77 ErrSyncRenameTableFailed, 78 ErrChangefeedUnretryable, 79 ErrCorruptedDataMutation, 80 ErrDispatcherFailed, 81 ErrColumnSelectorFailed, 82 83 ErrSinkURIInvalid, 84 ErrKafkaInvalidConfig, 85 ErrMySQLInvalidConfig, 86 ErrStorageSinkInvalidConfig, 87 } 88 89 // ShouldFailChangefeed returns true if an error is a changefeed not retry error. 90 func ShouldFailChangefeed(err error) bool { 91 for _, e := range changefeedUnRetryableErrors { 92 if e.Equal(err) { 93 return true 94 } 95 if code, ok := RFCCode(err); ok { 96 if code == e.RFCCode() { 97 return true 98 } 99 } 100 if strings.Contains(err.Error(), string(e.RFCCode())) { 101 return true 102 } 103 } 104 return false 105 } 106 107 // RFCCode returns a RFCCode from an error 108 func RFCCode(err error) (errors.RFCErrorCode, bool) { 109 type rfcCoder interface { 110 RFCCode() errors.RFCErrorCode 111 } 112 if terr, ok := err.(rfcCoder); ok { 113 return terr.RFCCode(), true 114 } 115 cause := errors.Unwrap(err) 116 if cause == nil { 117 return "", false 118 } 119 if terr, ok := cause.(rfcCoder); ok { 120 return terr.RFCCode(), true 121 } 122 return RFCCode(cause) 123 } 124 125 // IsDupEntryError checks if an error is a duplicate entry error. 126 func IsDupEntryError(err error) bool { 127 if err == nil { 128 return false 129 } 130 if ErrMySQLDuplicateEntry.Equal(err) { 131 return true 132 } 133 if code, ok := RFCCode(err); ok { 134 if code == ErrMySQLDuplicateEntry.RFCCode() { 135 return true 136 } 137 } 138 if strings.Contains(err.Error(), string(ErrMySQLDuplicateEntry.RFCCode())) { 139 return true 140 } 141 return false 142 } 143 144 // IsRetryableError check the error is safe or worth to retry 145 func IsRetryableError(err error) bool { 146 if err == nil { 147 return false 148 } 149 150 switch errors.Cause(err) { 151 case context.Canceled, context.DeadlineExceeded: 152 return false 153 } 154 return true 155 } 156 157 var cliUnprintableError = []*errors.Error{ErrCliAborted} 158 159 // IsCliUnprintableError returns true if the error should not be printed in cli. 160 func IsCliUnprintableError(err error) bool { 161 if err == nil { 162 return false 163 } 164 for _, e := range cliUnprintableError { 165 if strings.Contains(err.Error(), string(e.RFCCode())) { 166 return true 167 } 168 } 169 return false 170 } 171 172 // WrapChangefeedUnretryableErr wraps an error into ErrChangefeedUnRetryable. 173 func WrapChangefeedUnretryableErr(err error, args ...interface{}) error { 174 return WrapError(ErrChangefeedUnretryable, err, args...) 175 } 176 177 // IsContextCanceledError checks if an error is caused by context.Canceled. 178 func IsContextCanceledError(err error) bool { 179 return errors.Cause(err) == context.Canceled 180 } 181 182 // IsContextDeadlineExceededError checks if an error is caused by context.DeadlineExceeded. 183 func IsContextDeadlineExceededError(err error) bool { 184 return errors.Cause(err) == context.DeadlineExceeded 185 } 186 187 // httpStatusCodeMapping is a mapping from RFC error code to HTTP status code. 188 // It does not contain all RFC error codes, only the ones what we think 189 // are not just internal errors. 190 var httpStatusCodeMapping = map[errors.RFCErrorCode]int{ 191 ErrUnknown.RFCCode(): http.StatusInternalServerError, 192 ErrInvalidArgument.RFCCode(): http.StatusBadRequest, 193 ErrMasterNotReady.RFCCode(): http.StatusServiceUnavailable, 194 ErrJobNotFound.RFCCode(): http.StatusNotFound, 195 ErrJobAlreadyExists.RFCCode(): http.StatusConflict, 196 ErrJobAlreadyCanceled.RFCCode(): http.StatusBadRequest, 197 ErrJobNotTerminated.RFCCode(): http.StatusBadRequest, 198 ErrJobNotRunning.RFCCode(): http.StatusBadRequest, 199 ErrMetaStoreNotExists.RFCCode(): http.StatusNotFound, 200 ErrResourceAlreadyExists.RFCCode(): http.StatusConflict, 201 ErrIllegalResourcePath.RFCCode(): http.StatusBadRequest, 202 ErrResourceDoesNotExist.RFCCode(): http.StatusNotFound, 203 ErrResourceConflict.RFCCode(): http.StatusConflict, 204 } 205 206 // HTTPStatusCode returns the HTTP status code for the given error. 207 func HTTPStatusCode(err error) int { 208 if err == nil { 209 return http.StatusOK 210 } 211 rfcCode, ok := RFCCode(err) 212 if !ok { 213 if IsContextCanceledError(err) { 214 return 499 // Client Closed Request 215 } 216 if IsContextDeadlineExceededError(err) { 217 return http.StatusGatewayTimeout 218 } 219 return http.StatusInternalServerError 220 } 221 if code, ok := httpStatusCodeMapping[rfcCode]; ok { 222 return code 223 } 224 return http.StatusInternalServerError 225 } 226 227 // gRPCStatusCodeMapping is a mapping from RFC error code to gRPC status code. 228 // It does not contain all RFC error codes, only the ones what we think 229 // are not just internal errors. 230 var gRPCStatusCodeMapping = map[errors.RFCErrorCode]codes.Code{ 231 ErrUnknown.RFCCode(): codes.Unknown, 232 ErrInvalidArgument.RFCCode(): codes.InvalidArgument, 233 ErrMasterNotReady.RFCCode(): codes.Unavailable, 234 ErrUnknownExecutor.RFCCode(): codes.InvalidArgument, 235 ErrTombstoneExecutor.RFCCode(): codes.FailedPrecondition, 236 ErrJobNotFound.RFCCode(): codes.NotFound, 237 ErrJobAlreadyExists.RFCCode(): codes.AlreadyExists, 238 ErrJobAlreadyCanceled.RFCCode(): codes.FailedPrecondition, 239 ErrJobNotTerminated.RFCCode(): codes.FailedPrecondition, 240 ErrJobNotRunning.RFCCode(): codes.FailedPrecondition, 241 ErrMetaStoreNotExists.RFCCode(): codes.NotFound, 242 ErrResourceAlreadyExists.RFCCode(): codes.AlreadyExists, 243 ErrIllegalResourcePath.RFCCode(): codes.InvalidArgument, 244 ErrResourceDoesNotExist.RFCCode(): codes.NotFound, 245 ErrResourceConflict.RFCCode(): codes.FailedPrecondition, 246 } 247 248 // GRPCStatusCode returns the gRPC status code for the given error. 249 func GRPCStatusCode(err error) codes.Code { 250 if err == nil { 251 return codes.OK 252 } 253 if s, ok := status.FromError(err); ok { 254 return s.Code() 255 } 256 rfcCode, ok := RFCCode(err) 257 if !ok { 258 if IsContextCanceledError(err) { 259 return codes.Canceled 260 } 261 if IsContextDeadlineExceededError(err) { 262 return codes.DeadlineExceeded 263 } 264 return codes.Unknown 265 } 266 if code, ok := gRPCStatusCodeMapping[rfcCode]; ok { 267 return code 268 } 269 return codes.Internal 270 }