github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/errorutil/util.go (about) 1 // Copyright 2021 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 errorutil 15 16 import ( 17 "strings" 18 19 gmysql "github.com/go-sql-driver/mysql" 20 "github.com/pingcap/errors" 21 "github.com/pingcap/tidb/pkg/infoschema" 22 "github.com/pingcap/tidb/pkg/parser/mysql" 23 "github.com/pingcap/tidb/pkg/util/dbterror" 24 "github.com/pingcap/tidb/pkg/util/dbutil" 25 dmretry "github.com/pingcap/tiflow/dm/pkg/retry" 26 frameModel "github.com/pingcap/tiflow/engine/framework/model" 27 cerror "github.com/pingcap/tiflow/pkg/errors" 28 v3rpc "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" 29 ) 30 31 // IsIgnorableMySQLDDLError is used to check what error can be ignored 32 // we can get error code from: 33 // infoschema's error definition: https://github.com/pingcap/tidb/blob/master/infoschema/infoschema.go 34 // DDL's error definition: https://github.com/pingcap/tidb/blob/master/ddl/ddl.go 35 // tidb/mysql error code definition: https://github.com/pingcap/tidb/blob/master/mysql/errcode.go 36 func IsIgnorableMySQLDDLError(err error) bool { 37 err = errors.Cause(err) 38 mysqlErr, ok := err.(*gmysql.MySQLError) 39 if !ok { 40 return false 41 } 42 43 errCode := errors.ErrCode(mysqlErr.Number) 44 switch errCode { 45 case infoschema.ErrDatabaseExists.Code(), infoschema.ErrDatabaseDropExists.Code(), 46 infoschema.ErrTableExists.Code(), infoschema.ErrTableDropExists.Code(), 47 infoschema.ErrColumnExists.Code(), infoschema.ErrIndexExists.Code(), 48 infoschema.ErrKeyNotExists.Code(), dbterror.ErrCantDropFieldOrKey.Code(), 49 infoschema.ErrColumnNotExists.Code(), 50 mysql.ErrDupKeyName, mysql.ErrSameNamePartition, 51 mysql.ErrDropPartitionNonExistent, mysql.ErrMultiplePriKey: 52 return true 53 default: 54 return false 55 } 56 } 57 58 // IsRetryableEtcdError is used to check what error can be retried. 59 func IsRetryableEtcdError(err error) bool { 60 if err == nil { 61 return false 62 } 63 etcdErr := errors.Cause(err) 64 65 switch etcdErr { 66 // Etcd ResourceExhausted errors, may recover after some time 67 case v3rpc.ErrNoSpace, v3rpc.ErrTooManyRequests: 68 return true 69 // Etcd Unavailable errors, may be available after some time 70 // https://github.com/etcd-io/etcd/pull/9934/files#diff-6d8785d0c9eaf96bc3e2b29c36493c04R162-R167 71 // ErrStopped: 72 // one of the etcd nodes stopped from failure injection 73 // ErrNotCapable: 74 // capability check has not been done (in the beginning) 75 case v3rpc.ErrNoLeader, v3rpc.ErrLeaderChanged, v3rpc.ErrNotCapable, v3rpc.ErrStopped, v3rpc.ErrTimeout, 76 v3rpc.ErrTimeoutDueToLeaderFail, v3rpc.ErrGRPCTimeoutDueToConnectionLost, v3rpc.ErrUnhealthy: 77 return true 78 default: 79 } 80 // when the PD instance was deleted from the PD cluster, it may meet different errors. 81 // retry on such error make cdc robust to PD / ETCD cluster member removal. 82 // we should tolerant such case to make cdc robust to PD / ETCD cluster member change. 83 // see: https://github.com/etcd-io/etcd/blob/ae36a577d7be/raft/node.go#L35 84 if strings.Contains(etcdErr.Error(), "raft: stopped") { 85 return true 86 } 87 // see: https://github.com/pingcap/tiflow/issues/6720 88 if strings.Contains(etcdErr.Error(), "received prior goaway: code: NO_ERROR") { 89 return true 90 } 91 92 // this may happen if the PD instance shutdown by `kill -9`, no matter the instance is the leader or not. 93 if strings.Contains(etcdErr.Error(), "connection reset by peer") { 94 return true 95 } 96 return false 97 } 98 99 // IsRetryableDMLError check if the error is a retryable dml error. 100 func IsRetryableDMLError(err error) bool { 101 if !cerror.IsRetryableError(err) { 102 return false 103 } 104 // Check if the error is connection errors that can retry safely. 105 if dmretry.IsConnectionError(err) { 106 return true 107 } 108 // Check if the error is a retriable TiDB error or MySQL error. 109 return dbutil.IsRetryableError(err) 110 } 111 112 // IsRetryableDDLError check if the error is a retryable ddl error. 113 func IsRetryableDDLError(err error) bool { 114 if IsRetryableDMLError(err) { 115 return true 116 } 117 118 // All DDLs should be idempotent in theory. 119 if dmretry.IsUnretryableConnectionError(err) { 120 return true 121 } 122 123 err = errors.Cause(err) 124 mysqlErr, ok := err.(*gmysql.MySQLError) 125 if !ok { 126 return false 127 } 128 129 // If the error is in the black list, return false. 130 switch mysqlErr.Number { 131 case mysql.ErrAccessDenied, 132 mysql.ErrDBaccessDenied, 133 mysql.ErrSyntax, 134 mysql.ErrParse, 135 mysql.ErrNoDB, 136 mysql.ErrBadDB, 137 mysql.ErrNoSuchTable, 138 mysql.ErrNoSuchIndex, 139 mysql.ErrKeyColumnDoesNotExits, 140 mysql.ErrWrongColumnName, 141 mysql.ErrPartitionMgmtOnNonpartitioned, 142 mysql.ErrNonuniqTable: 143 return false 144 } 145 return true 146 } 147 148 // IsAccessDeniedError checks if the error is an access denied error. 149 func IsAccessDeniedError(err error) bool { 150 err = errors.Cause(err) 151 mysqlErr, ok := err.(*gmysql.MySQLError) 152 if !ok { 153 return false 154 } 155 return mysqlErr.Number == mysql.ErrAccessDenied || 156 mysqlErr.Number == mysql.ErrAccessDeniedNoPassword 157 } 158 159 // IsSyncPointIgnoreError returns whether the error is ignorable for syncpoint. 160 func IsSyncPointIgnoreError(err error) bool { 161 err = errors.Cause(err) 162 mysqlErr, ok := err.(*gmysql.MySQLError) 163 if !ok { 164 return false 165 } 166 // We should ignore the error when the downstream has no 167 // such system variable for compatibility. 168 return mysqlErr.Number == mysql.ErrUnknownSystemVariable 169 } 170 171 // ConvertErr converts an error to a specific error by worker type. 172 func ConvertErr(tp frameModel.WorkerType, err error) error { 173 switch tp { 174 case frameModel.DMJobMaster: 175 err = cerror.ToDMError(err) 176 default: 177 } 178 return err 179 }