github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/topic/retriable_error.go (about) 1 package topic 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "time" 8 9 "github.com/ydb-platform/ydb-go-sdk/v3/internal/backoff" 10 "github.com/ydb-platform/ydb-go-sdk/v3/internal/value" 11 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 12 "github.com/ydb-platform/ydb-go-sdk/v3/retry" 13 ) 14 15 const ( 16 DefaultStartTimeout = value.InfiniteDuration 17 connectionEstablishedTimeout = time.Minute 18 ) 19 20 var errNil = xerrors.Wrap(errors.New("nil error is not retrieable")) 21 22 type RetrySettings struct { 23 StartTimeout time.Duration // Full retry timeout 24 CheckError PublicCheckErrorRetryFunction 25 } 26 27 type PublicCheckErrorRetryFunction func(errInfo PublicCheckErrorRetryArgs) PublicCheckRetryResult 28 29 type PublicCheckErrorRetryArgs struct { 30 Error error 31 } 32 33 func NewCheckRetryArgs(err error) PublicCheckErrorRetryArgs { 34 return PublicCheckErrorRetryArgs{ 35 Error: err, 36 } 37 } 38 39 type PublicCheckRetryResult struct { 40 val int 41 } 42 43 var ( 44 PublicRetryDecisionDefault = PublicCheckRetryResult{val: 0} 45 PublicRetryDecisionRetry = PublicCheckRetryResult{val: 1} 46 PublicRetryDecisionStop = PublicCheckRetryResult{val: 2} //nolint:gomnd 47 ) 48 49 func CheckResetReconnectionCounters(lastTry, now time.Time, connectionTimeout time.Duration) bool { 50 const resetAttemptEmpiricalCoefficient = 10 51 if connectionTimeout == value.InfiniteDuration { 52 return now.Sub(lastTry) > connectionEstablishedTimeout 53 } 54 55 return now.Sub(lastTry) > connectionTimeout*resetAttemptEmpiricalCoefficient 56 } 57 58 // RetryDecision check if err is retriable. 59 // if return nil stopRetryReason - err can be retried 60 // if return non nil stopRetryReason - err is not retriable and stopRetryReason contains reason, 61 // which should be used instead of err 62 func RetryDecision(checkErr error, settings RetrySettings, retriesDuration time.Duration) ( 63 _ backoff.Backoff, 64 stopRetryReason error, 65 ) { 66 // nil is not error and doesn't need retry it. 67 if checkErr == nil { 68 return nil, xerrors.WithStackTrace(errNil) 69 } 70 71 // eof is retriable for topic 72 if errors.Is(checkErr, io.EOF) && xerrors.RetryableError(checkErr) == nil { 73 checkErr = xerrors.Retryable(checkErr, xerrors.WithName("TopicEOF")) 74 } 75 76 if retriesDuration > settings.StartTimeout { 77 return nil, fmt.Errorf("ydb: topic reader reconnection timeout, last error: %w", xerrors.Unretryable(checkErr)) 78 } 79 80 mode := retry.Check(checkErr) 81 82 decision := PublicRetryDecisionDefault 83 if settings.CheckError != nil { 84 decision = settings.CheckError(NewCheckRetryArgs(checkErr)) 85 } 86 87 switch decision { 88 case PublicRetryDecisionDefault: 89 isRetriable := mode.MustRetry(true) 90 if !isRetriable { 91 return nil, fmt.Errorf("ydb: topic reader unretriable error: %w", xerrors.Unretryable(checkErr)) 92 } 93 case PublicRetryDecisionRetry: 94 // pass 95 case PublicRetryDecisionStop: 96 return nil, fmt.Errorf( 97 "ydb: topic reader unretriable error by check error callback: %w", 98 xerrors.Unretryable(checkErr), 99 ) 100 default: 101 panic(fmt.Errorf("unexpected retry decision: %v", decision)) 102 } 103 104 // checkErr is retryable error 105 106 switch mode.BackoffType() { 107 case backoff.TypeFast: 108 return backoff.Fast, nil 109 default: 110 return backoff.Slow, nil 111 } 112 }