github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/topic/retriable_error_test.go (about) 1 package topic 2 3 import ( 4 "fmt" 5 "io" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/require" 10 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" 11 grpcCodes "google.golang.org/grpc/codes" 12 grpcStatus "google.golang.org/grpc/status" 13 14 "github.com/ydb-platform/ydb-go-sdk/v3/internal/backoff" 15 "github.com/ydb-platform/ydb-go-sdk/v3/internal/value" 16 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 17 ) 18 19 func TestCheckRetryMode(t *testing.T) { 20 fastError := xerrors.Transport(grpcStatus.Error(grpcCodes.Unavailable, "")) 21 slowError := xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_OVERLOADED)) 22 unretriable := xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAUTHORIZED)) 23 24 table := []struct { 25 name string 26 err error 27 settings RetrySettings 28 duration time.Duration 29 resBackoff backoff.Backoff 30 resRetriable bool 31 }{ 32 { 33 name: "OK", 34 err: nil, 35 settings: RetrySettings{}, 36 duration: 0, 37 resBackoff: nil, 38 resRetriable: false, 39 }, 40 { 41 name: "RetryRetriableErrorFast", 42 err: fastError, 43 settings: RetrySettings{}, 44 duration: 0, 45 resBackoff: backoff.Fast, 46 resRetriable: true, 47 }, 48 { 49 name: "RetryRetriableErrorFastWithTimeout", 50 err: fastError, 51 settings: RetrySettings{ 52 StartTimeout: time.Second, 53 }, 54 duration: time.Second * 2, 55 resBackoff: nil, 56 resRetriable: false, 57 }, 58 { 59 name: "RetryRetriableErrorSlow", 60 err: slowError, 61 settings: RetrySettings{}, 62 duration: 0, 63 resBackoff: backoff.Slow, 64 resRetriable: true, 65 }, 66 { 67 name: "RetryRetriableErrorSlowWithTimeout", 68 err: slowError, 69 settings: RetrySettings{ 70 StartTimeout: time.Second, 71 }, 72 duration: time.Second * 2, 73 resBackoff: nil, 74 resRetriable: false, 75 }, 76 { 77 name: "UnretriableError", 78 err: unretriable, 79 settings: RetrySettings{}, 80 duration: 0, 81 resBackoff: nil, 82 resRetriable: false, 83 }, 84 { 85 name: "UserOverrideFastErrorDefault", 86 err: fastError, 87 settings: RetrySettings{ 88 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 89 return PublicRetryDecisionDefault 90 }, 91 }, 92 duration: 0, 93 resBackoff: backoff.Fast, 94 resRetriable: true, 95 }, 96 { 97 name: "UserOverrideFastErrorRetry", 98 err: fastError, 99 settings: RetrySettings{ 100 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 101 return PublicRetryDecisionRetry 102 }, 103 }, 104 duration: 0, 105 resBackoff: backoff.Fast, 106 resRetriable: true, 107 }, 108 { 109 name: "UserOverrideFastErrorStop", 110 err: fastError, 111 settings: RetrySettings{ 112 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 113 return PublicRetryDecisionStop 114 }, 115 }, 116 duration: 0, 117 resBackoff: nil, 118 resRetriable: false, 119 }, 120 { 121 name: "UserOverrideSlowErrorDefault", 122 err: slowError, 123 settings: RetrySettings{ 124 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 125 return PublicRetryDecisionDefault 126 }, 127 }, 128 duration: 0, 129 resBackoff: backoff.Slow, 130 resRetriable: true, 131 }, 132 { 133 name: "UserOverrideSlowErrorRetry", 134 err: slowError, 135 settings: RetrySettings{ 136 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 137 return PublicRetryDecisionRetry 138 }, 139 }, 140 duration: 0, 141 resBackoff: backoff.Slow, 142 resRetriable: true, 143 }, 144 { 145 name: "UserOverrideSlowErrorStop", 146 err: slowError, 147 settings: RetrySettings{ 148 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 149 return PublicRetryDecisionStop 150 }, 151 }, 152 duration: 0, 153 resBackoff: nil, 154 resRetriable: false, 155 }, 156 { 157 name: "UserOverrideUnretriableErrorDefault", 158 err: xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAUTHORIZED)), 159 settings: RetrySettings{ 160 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 161 return PublicRetryDecisionDefault 162 }, 163 }, 164 duration: 0, 165 resBackoff: nil, 166 resRetriable: false, 167 }, 168 { 169 name: "UserOverrideUnretriableErrorRetry", 170 err: xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAUTHORIZED)), 171 settings: RetrySettings{ 172 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 173 return PublicRetryDecisionRetry 174 }, 175 }, 176 duration: 0, 177 resBackoff: backoff.Slow, 178 resRetriable: true, 179 }, 180 { 181 name: "UserOverrideUnretriableErrorStop", 182 err: xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAUTHORIZED)), 183 settings: RetrySettings{ 184 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 185 return PublicRetryDecisionStop 186 }, 187 }, 188 duration: 0, 189 resBackoff: nil, 190 resRetriable: false, 191 }, 192 { 193 name: "UserOverrideFastErrorRetryWithTimeout", 194 err: xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAUTHORIZED)), 195 settings: RetrySettings{ 196 StartTimeout: time.Second, 197 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 198 return PublicRetryDecisionRetry 199 }, 200 }, 201 duration: time.Second * 2, 202 resBackoff: nil, 203 resRetriable: false, 204 }, 205 { 206 name: "NotCallForNil", 207 err: nil, 208 settings: RetrySettings{ 209 StartTimeout: time.Second, 210 CheckError: func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult { 211 panic("must not call for nil err") 212 }, 213 }, 214 duration: 0, 215 resBackoff: nil, 216 resRetriable: false, 217 }, 218 { 219 name: "EOF", // Issue https://github.com/ydb-platform/ydb-go-sdk/issues/754 220 err: fmt.Errorf("test wrap: %w", io.EOF), 221 settings: RetrySettings{}, 222 duration: 0, 223 resBackoff: backoff.Slow, 224 resRetriable: true, 225 }, 226 } 227 228 for _, test := range table { 229 t.Run(test.name, func(t *testing.T) { 230 resBackoff, retriable := CheckRetryMode(test.err, test.settings, test.duration) 231 require.Equal(t, test.resBackoff, resBackoff) 232 require.Equal(t, test.resRetriable, retriable) 233 }) 234 } 235 } 236 237 func TestCheckResetReconnectionCounters(t *testing.T) { 238 now := time.Now() 239 table := []struct { 240 name string 241 lastTry time.Time 242 connectionTimeout time.Duration 243 shouldReset bool 244 }{ 245 { 246 name: "RecentLastTryWithInfiniteConnectionTimeout", 247 lastTry: now.Add(-30 * time.Second), 248 connectionTimeout: value.InfiniteDuration, 249 shouldReset: false, 250 }, 251 { 252 name: "OldLastTryWithInfiniteConnectionTimeout", 253 lastTry: now.Add(-30 * time.Minute), 254 connectionTimeout: value.InfiniteDuration, 255 shouldReset: true, 256 }, 257 { 258 name: "LastTryLessThanConnectionTimeout", 259 lastTry: now.Add(-30 * time.Second), 260 connectionTimeout: time.Minute, 261 shouldReset: false, 262 }, 263 { 264 name: "LastTryGreaterThanConnectionTimeout", 265 lastTry: now.Add(-time.Hour), 266 connectionTimeout: time.Minute, 267 shouldReset: true, 268 }, 269 } 270 271 for _, test := range table { 272 t.Run(test.name, func(t *testing.T) { 273 shouldReset := CheckResetReconnectionCounters(test.lastTry, now, test.connectionTimeout) 274 require.Equal(t, test.shouldReset, shouldReset) 275 }) 276 } 277 }