github.com/m3db/m3@v1.5.0/src/dbnode/client/errors.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package client 22 23 import ( 24 "fmt" 25 "sync" 26 27 "github.com/uber/tchannel-go" 28 29 "github.com/m3db/m3/src/dbnode/generated/thrift/rpc" 30 tterrors "github.com/m3db/m3/src/dbnode/network/server/tchannelthrift/errors" 31 xerrors "github.com/m3db/m3/src/x/errors" 32 ) 33 34 // IsInternalServerError determines if the error is an internal server error. 35 func IsInternalServerError(err error) bool { 36 for err != nil { 37 if e, ok := err.(*rpc.Error); ok && tterrors.IsInternalError(e) { 38 return true 39 } 40 err = xerrors.InnerError(err) 41 } 42 return false 43 } 44 45 // IsBadRequestError determines if the error is a bad request error. 46 func IsBadRequestError(err error) bool { 47 for err != nil { 48 if e, ok := err.(*rpc.Error); ok && tterrors.IsBadRequestError(e) { 49 return true 50 } 51 if e := xerrors.GetInnerInvalidParamsError(err); e != nil { 52 return true 53 } 54 err = xerrors.InnerError(err) 55 } 56 return false 57 } 58 59 // IsResourceExhaustedError determines if the error is a resource exhausted error. 60 func IsResourceExhaustedError(err error) bool { 61 for err != nil { 62 if e, ok := err.(*rpc.Error); ok && tterrors.IsResourceExhaustedErrorFlag(e) { //nolint:errorlint 63 return true 64 } 65 if e := xerrors.GetInnerResourceExhaustedError(err); e != nil { 66 return true 67 } 68 err = xerrors.InnerError(err) 69 } 70 return false 71 } 72 73 // IsTimeoutError determines if the error is a timeout. 74 func IsTimeoutError(err error) bool { 75 for err != nil { 76 // nolint:errorlint 77 if e, ok := err.(*rpc.Error); ok { 78 if tterrors.IsTimeoutError(e) { 79 return true 80 } 81 } 82 // Need to also check if the message is directly the tchannel ErrTimeout error. 83 // This is because those errors can come through at the tchannel layer, 84 // rather than in our application layer, meaning we don't have any 85 // means to intercept / set the SERVER_TIMEOUT flag. 86 if err.Error() == tchannel.ErrTimeout.Error() { 87 return true 88 } 89 err = xerrors.InnerError(err) 90 } 91 return false 92 } 93 94 // IsConsistencyResultError determines if the error is a consistency result error. 95 func IsConsistencyResultError(err error) bool { 96 for err != nil { 97 if _, ok := err.(consistencyResultErr); ok { //nolint:errorlint 98 return true 99 } 100 err = xerrors.InnerError(err) 101 } 102 return false 103 } 104 105 // NumResponded returns how many nodes responded for a given error 106 func NumResponded(err error) int { 107 for err != nil { 108 if e, ok := err.(consistencyResultError); ok { 109 return e.numResponded() 110 } 111 err = xerrors.InnerError(err) 112 } 113 return 0 114 } 115 116 // NumSuccess returns how many nodes responded with success for a given error 117 func NumSuccess(err error) int { 118 for err != nil { 119 if e, ok := err.(consistencyResultError); ok { 120 return e.numSuccess() 121 } 122 err = xerrors.InnerError(err) 123 } 124 return 0 125 } 126 127 // NumError returns how many nodes responded with error for a given error 128 func NumError(err error) int { 129 for err != nil { 130 if e, ok := err.(consistencyResultError); ok { 131 return e.numResponded() - 132 e.numSuccess() 133 } 134 err = xerrors.InnerError(err) 135 } 136 return 0 137 } 138 139 type hostNotAvailableError struct { 140 err error 141 } 142 143 func (h hostNotAvailableError) Error() string { 144 return h.err.Error() 145 } 146 147 func newHostNotAvailableError(err error) error { 148 return xerrors.NewNonRetryableError(hostNotAvailableError{err: err}) 149 } 150 151 func isHostNotAvailableError(err error) bool { 152 inner := xerrors.GetInnerNonRetryableError(err) 153 if inner == nil { 154 return false 155 } 156 _, ok := inner.(hostNotAvailableError) 157 return ok 158 } 159 160 type consistencyResultError interface { 161 error 162 xerrors.ContainedError 163 164 numResponded() int 165 numSuccess() int 166 } 167 168 type consistencyResultErr struct { 169 level fmt.Stringer 170 success int 171 enqueued int 172 responded int 173 topLevelErr error 174 errs []error 175 } 176 177 func newConsistencyResultError( 178 level fmt.Stringer, 179 enqueued, responded int, 180 errs []error, 181 ) consistencyResultError { 182 // NB(r): if any errors are bad request errors, encapsulate that error 183 // to ensure the error itself is wholly classified as a bad request error 184 var topLevelErr error 185 for i := 0; i < len(errs); i++ { 186 if topLevelErr == nil { 187 topLevelErr = errs[i] 188 continue 189 } 190 if IsBadRequestError(errs[i]) { 191 topLevelErr = errs[i] 192 break 193 } 194 if IsTimeoutError(errs[i]) { 195 topLevelErr = errs[i] 196 // Still continue iterating since bad request errors take precedence. 197 continue 198 } 199 } 200 return consistencyResultErr{ 201 level: level, 202 success: enqueued - len(errs), 203 enqueued: enqueued, 204 responded: responded, 205 topLevelErr: topLevelErr, 206 errs: append([]error(nil), errs...), 207 } 208 } 209 210 func (e consistencyResultErr) InnerError() error { 211 return e.topLevelErr 212 } 213 214 func (e consistencyResultErr) Error() string { 215 return fmt.Sprintf( 216 "failed to meet consistency level %s with %d/%d success, "+ 217 "%d nodes responded, errors: %v", 218 e.level.String(), e.success, e.enqueued, e.responded, e.errs) 219 } 220 221 func (e consistencyResultErr) numResponded() int { 222 return e.responded 223 } 224 225 func (e consistencyResultErr) numSuccess() int { 226 return e.success 227 } 228 229 type syncAbortableErrorsMap struct { 230 sync.RWMutex 231 errors map[int]error 232 abortError error 233 } 234 235 func newSyncAbortableErrorsMap() *syncAbortableErrorsMap { 236 return &syncAbortableErrorsMap{ 237 errors: make(map[int]error), 238 } 239 } 240 241 func (e *syncAbortableErrorsMap) setError(idx int, err error) { 242 e.Lock() 243 e.errors[idx] = err 244 e.Unlock() 245 } 246 247 func (e *syncAbortableErrorsMap) getErrors() []error { 248 var result []error 249 e.RLock() 250 for _, err := range e.errors { 251 if err == nil { 252 continue 253 } 254 result = append(result, err) 255 } 256 e.RUnlock() 257 return result 258 } 259 260 func (e *syncAbortableErrorsMap) setAbortError(err error) { 261 e.Lock() 262 e.abortError = err 263 e.Unlock() 264 } 265 func (e *syncAbortableErrorsMap) getAbortError() error { 266 e.RLock() 267 result := e.abortError 268 e.RUnlock() 269 return result 270 }