github.com/go-playground/pkg/v5@v5.29.1/errors/do.go (about)

     1  //go:build go1.18
     2  // +build go1.18
     3  
     4  package errorsext
     5  
     6  import (
     7  	"context"
     8  
     9  	optionext "github.com/go-playground/pkg/v5/values/option"
    10  	resultext "github.com/go-playground/pkg/v5/values/result"
    11  )
    12  
    13  // RetryableFn is a function that can be retried.
    14  type RetryableFn[T, E any] func(ctx context.Context) resultext.Result[T, E]
    15  
    16  // IsRetryableFn is called to determine if the error is retryable and optionally returns the reason for logging and metrics.
    17  type IsRetryableFn[E any] func(err E) (reason string, isRetryable bool)
    18  
    19  // OnRetryFn is called after IsRetryableFn returns true and before the retry is attempted.
    20  //
    21  // this allows for interception, short-circuiting and adding of backoff strategies.
    22  type OnRetryFn[E any] func(ctx context.Context, originalErr E, reason string, attempt int) optionext.Option[E]
    23  
    24  // DoRetryable will execute the provided functions code and automatically retry using the provided retry function.
    25  //
    26  // Deprecated: use `errorsext.Retrier` instead which corrects design issues with the current implementation.
    27  func DoRetryable[T, E any](ctx context.Context, isRetryFn IsRetryableFn[E], onRetryFn OnRetryFn[E], fn RetryableFn[T, E]) resultext.Result[T, E] {
    28  	var attempt int
    29  	for {
    30  		result := fn(ctx)
    31  		if result.IsErr() {
    32  			err := result.Err()
    33  			if reason, isRetryable := isRetryFn(err); isRetryable {
    34  				if opt := onRetryFn(ctx, err, reason, attempt); opt.IsSome() {
    35  					return resultext.Err[T, E](opt.Unwrap())
    36  				}
    37  				attempt++
    38  				continue
    39  			}
    40  		}
    41  		return result
    42  	}
    43  }