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  }