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  }