github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/changefeedccl/errors.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Licensed as a CockroachDB Enterprise file under the Cockroach Community 4 // License (the "License"); you may not use this file except in compliance with 5 // the License. You may obtain a copy of the License at 6 // 7 // https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt 8 9 package changefeedccl 10 11 import ( 12 "fmt" 13 "reflect" 14 "strings" 15 16 "github.com/cockroachdb/errors" 17 ) 18 19 const retryableErrorString = "retryable changefeed error" 20 21 type retryableError struct { 22 wrapped error 23 } 24 25 // MarkRetryableError wraps the given error, marking it as retryable to 26 // changefeeds. 27 func MarkRetryableError(e error) error { 28 return &retryableError{wrapped: e} 29 } 30 31 // Error implements the error interface. 32 func (e *retryableError) Error() string { 33 return fmt.Sprintf("%s: %s", retryableErrorString, e.wrapped.Error()) 34 } 35 36 // Cause implements the github.com/pkg/errors.causer interface. 37 func (e *retryableError) Cause() error { return e.wrapped } 38 39 // Unwrap implements the github.com/golang/xerrors.Wrapper interface, which is 40 // planned to be moved to the stdlib in go 1.13. 41 func (e *retryableError) Unwrap() error { return e.wrapped } 42 43 // IsRetryableError returns true if the supplied error, or any of its parent 44 // causes, is a IsRetryableError. 45 func IsRetryableError(err error) bool { 46 if err == nil { 47 return false 48 } 49 if errors.HasType(err, (*retryableError)(nil)) { 50 return true 51 } 52 53 // TODO(knz): this is a bad implementation. Make it go away 54 // by avoiding string comparisons. 55 56 errStr := err.Error() 57 if strings.Contains(errStr, retryableErrorString) { 58 // If a RetryableError occurs on a remote node, DistSQL serializes it such 59 // that we can't recover the structure and we have to rely on this 60 // unfortunate string comparison. 61 return true 62 } 63 if strings.Contains(errStr, `rpc error`) { 64 // When a crdb node dies, any DistSQL flows with processors scheduled on 65 // it get an error with "rpc error" in the message from the call to 66 // `(*DistSQLPlanner).Run`. 67 return true 68 } 69 return false 70 } 71 72 // MaybeStripRetryableErrorMarker performs some minimal attempt to clean the 73 // RetryableError marker out. This won't do anything if the RetryableError 74 // itself has been wrapped, but that's okay, we'll just have an uglier string. 75 func MaybeStripRetryableErrorMarker(err error) error { 76 // The following is a hack to work around the error cast linter. 77 // What we're doing here is really not kosher; this function 78 // has no business in assuming that the retryableError{} wrapper 79 // has not been wrapped already. We could even expect that 80 // it gets wrapped in the common case. 81 // TODO(knz): Remove/replace this. 82 if reflect.TypeOf(err) == retryableErrorType { 83 err = errors.UnwrapOnce(err) 84 } 85 return err 86 } 87 88 var retryableErrorType = reflect.TypeOf((*retryableError)(nil))