github.com/mondo192/jfrog-client-go@v1.0.0/utils/retryexecutor.go (about) 1 package utils 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "github.com/mondo192/jfrog-client-go/utils/errorutils" 8 "time" 9 10 "github.com/mondo192/jfrog-client-go/utils/log" 11 ) 12 13 type ExecutionHandlerFunc func() (bool, error) 14 15 type RetryExecutor struct { 16 // The context 17 Context context.Context 18 19 // The amount of retries to perform. 20 MaxRetries int 21 22 // Number of milliseconds to sleep between retries. 23 RetriesIntervalMilliSecs int 24 25 // Message to display when retrying. 26 ErrorMessage string 27 28 // Prefix to print at the beginning of each log. 29 LogMsgPrefix string 30 31 // ExecutionHandler is the operation to run with retries. 32 ExecutionHandler ExecutionHandlerFunc 33 } 34 35 func (runner *RetryExecutor) Execute() error { 36 var err error 37 var shouldRetry bool 38 for i := 0; i <= runner.MaxRetries; i++ { 39 // Run ExecutionHandler 40 shouldRetry, err = runner.ExecutionHandler() 41 42 // If we should not retry, return. 43 if !shouldRetry { 44 return err 45 } 46 if cancelledErr := runner.checkCancelled(); cancelledErr != nil { 47 return cancelledErr 48 } 49 50 // Print retry log message 51 runner.LogRetry(i, err) 52 53 // Going to sleep for RetryInterval milliseconds 54 if runner.RetriesIntervalMilliSecs > 0 && i < runner.MaxRetries { 55 time.Sleep(time.Millisecond * time.Duration(runner.RetriesIntervalMilliSecs)) 56 } 57 } 58 // If the error is not nil, return it and log the timeout message. Otherwise, generate new error. 59 if err != nil { 60 log.Info(runner.getTimeoutErrorMsg()) 61 return err 62 } 63 return errorutils.CheckError(RetryExecutorTimeoutError{runner.getTimeoutErrorMsg()}) 64 } 65 66 // Error of this type will be returned if the executor reaches timeout and no other error is returned by the execution handler. 67 type RetryExecutorTimeoutError struct { 68 errMsg string 69 } 70 71 func (retryErr RetryExecutorTimeoutError) Error() string { 72 return retryErr.errMsg 73 } 74 75 func (runner *RetryExecutor) getTimeoutErrorMsg() string { 76 prefix := "" 77 if runner.LogMsgPrefix != "" { 78 prefix = runner.LogMsgPrefix + " " 79 } 80 return fmt.Sprintf("%sexecutor timeout after %v attempts with %v milliseconds wait intervals", prefix, runner.MaxRetries, runner.RetriesIntervalMilliSecs) 81 } 82 83 func (runner *RetryExecutor) LogRetry(attemptNumber int, err error) { 84 message := fmt.Sprintf("%s(Attempt %v)", runner.LogMsgPrefix, attemptNumber+1) 85 if runner.ErrorMessage != "" { 86 message = fmt.Sprintf("%s - %s", message, runner.ErrorMessage) 87 } 88 if err != nil { 89 message = fmt.Sprintf("%s: %s", message, err.Error()) 90 } 91 92 if err != nil || runner.ErrorMessage != "" { 93 log.Warn(message) 94 } else { 95 log.Debug(message) 96 } 97 } 98 99 func (runner *RetryExecutor) checkCancelled() error { 100 if runner.Context == nil { 101 return nil 102 } 103 contextErr := runner.Context.Err() 104 if errors.Is(contextErr, context.Canceled) { 105 log.Info("Retry executor was cancelled") 106 return contextErr 107 } 108 return nil 109 }