github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/xerrors/retryable.go (about) 1 package xerrors 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/ydb-platform/ydb-go-sdk/v3/internal/backoff" 8 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring" 9 ) 10 11 type retryableError struct { 12 name string 13 err error 14 backoffType backoff.Type 15 isRetryObjectValid bool 16 code int32 17 traceID string 18 } 19 20 func (re *retryableError) Code() int32 { 21 return re.code 22 } 23 24 func (re *retryableError) Name() string { 25 return "retryable/" + re.name 26 } 27 28 func (re *retryableError) Type() Type { 29 return TypeRetryable 30 } 31 32 func (re *retryableError) BackoffType() backoff.Type { 33 return re.backoffType 34 } 35 36 func (re *retryableError) IsRetryObjectValid() bool { 37 return re.isRetryObjectValid 38 } 39 40 func (re *retryableError) Error() string { 41 b := xstring.Buffer() 42 defer b.Free() 43 b.WriteString(re.Name()) 44 fmt.Fprintf(b, " (code = %d, source error = %q", re.code, re.err.Error()) 45 if len(re.traceID) > 0 { 46 fmt.Fprintf(b, ", traceID: %q", re.traceID) 47 } 48 b.WriteString(")") 49 50 return b.String() 51 } 52 53 func (re *retryableError) Unwrap() error { 54 return re.err 55 } 56 57 type RetryableErrorOption interface { 58 applyToRetryableError(re *retryableError) 59 } 60 61 var ( 62 _ RetryableErrorOption = backoffOption{} 63 _ RetryableErrorOption = nameOption("") 64 _ RetryableErrorOption = invalidObjectOption{} 65 ) 66 67 type backoffOption struct { 68 backoffType backoff.Type 69 } 70 71 func (t backoffOption) applyToRetryableError(re *retryableError) { 72 re.backoffType = t.backoffType 73 } 74 75 func WithBackoff(t backoff.Type) backoffOption { 76 return backoffOption{backoffType: t} 77 } 78 79 type nameOption string 80 81 func (name nameOption) applyToRetryableError(re *retryableError) { 82 re.name = string(name) 83 } 84 85 func WithName(name string) nameOption { 86 return nameOption(name) 87 } 88 89 type invalidObjectOption struct{} 90 91 func (invalidObjectOption) applyToRetryableError(re *retryableError) { 92 re.isRetryObjectValid = false 93 } 94 95 func InvalidObject() invalidObjectOption { 96 return invalidObjectOption{} 97 } 98 99 func Retryable(err error, opts ...RetryableErrorOption) error { 100 if err == nil { 101 return nil 102 } 103 var ( 104 e Error 105 re = &retryableError{ 106 err: err, 107 name: "CUSTOM", 108 code: -1, 109 isRetryObjectValid: true, 110 } 111 ) 112 if As(err, &e) { 113 re.backoffType = e.BackoffType() 114 re.isRetryObjectValid = e.IsRetryObjectValid() 115 re.code = e.Code() 116 re.name = e.Name() 117 } 118 for _, opt := range opts { 119 if opt != nil { 120 opt.applyToRetryableError(re) 121 } 122 } 123 124 return re 125 } 126 127 // RetryableError return Error if err is retriable error, else nil 128 func RetryableError(err error) Error { 129 var unretriableErr unretryableError 130 if errors.As(err, &unretriableErr) { 131 return nil 132 } 133 134 var e *retryableError 135 if errors.As(err, &e) { 136 return e 137 } 138 139 return nil 140 } 141 142 func Unretryable(err error) unretryableError { 143 return unretryableError{err} 144 } 145 146 type unretryableError struct { 147 error 148 } 149 150 func (e unretryableError) Unwrap() error { 151 return e.error 152 } 153 154 func IsRetryableError(err error) bool { 155 if err == nil { 156 return true 157 } 158 159 var e *retryableError 160 if errors.As(err, &e) { 161 return e != nil 162 } 163 164 return false 165 }