gitee.com/sy_183/go-common@v1.0.5-0.20231205030221-958cfe129b47/lifecycle.v2/retry/retry.go (about)

     1  package retry
     2  
     3  import (
     4  	"errors"
     5  	"gitee.com/sy_183/go-common/timer"
     6  	"math"
     7  	"time"
     8  )
     9  
    10  var ErrInterrupted = errors.New("retry interrupted")
    11  
    12  type RetryContext struct {
    13  	Do func() error
    14  
    15  	Interval time.Duration
    16  
    17  	Delay func(ctx *RetryContext) time.Duration
    18  
    19  	Interrupter chan struct{}
    20  
    21  	MaxRetry int
    22  
    23  	Retrievable func(ctx *RetryContext) bool
    24  
    25  	Ignorable func(ctx *RetryContext) bool
    26  
    27  	Error error
    28  
    29  	Retry int
    30  }
    31  
    32  type Retry RetryContext
    33  
    34  func MakeRetry(retry Retry) *RetryContext {
    35  	c := new(RetryContext)
    36  	*c = RetryContext(retry)
    37  	return c
    38  }
    39  
    40  func (c *RetryContext) do(retryTimer *timer.Timer) bool {
    41  	maxRetry := c.MaxRetry
    42  	if maxRetry < 0 {
    43  		maxRetry = math.MaxInt
    44  	}
    45  	// do func
    46  	if c.Error = c.Do(); c.Error != nil {
    47  		// check error ignorable
    48  		if ignorable := c.Ignorable; ignorable != nil {
    49  			if ignorable(c) {
    50  				c.Error = nil
    51  				return false
    52  			}
    53  		}
    54  		if c.Error == ErrInterrupted {
    55  			return false
    56  		}
    57  
    58  		// check retrievable
    59  		if c.Retry >= maxRetry {
    60  			return false
    61  		}
    62  		if retrievable := c.Retrievable; retrievable != nil {
    63  			if !retrievable(c) {
    64  				return false
    65  			}
    66  		}
    67  		c.Retry++
    68  
    69  		// get retry delay
    70  		delay := c.Interval
    71  		if c.Delay != nil {
    72  			delay = c.Delay(c)
    73  		}
    74  		if delay <= 0 {
    75  			delay = time.Second
    76  		}
    77  
    78  		retryTimer.After(delay)
    79  		return true
    80  	}
    81  	return false
    82  }
    83  
    84  func (c *RetryContext) Todo() error {
    85  	retryTimer := timer.NewTimer(make(chan struct{}))
    86  	defer retryTimer.Stop()
    87  
    88  	if retry := c.do(retryTimer); c.Error == nil {
    89  		return nil
    90  	} else if !retry {
    91  		return c.Error
    92  	}
    93  
    94  	for {
    95  		select {
    96  		case <-retryTimer.C:
    97  			if retry := c.do(retryTimer); c.Error == nil {
    98  				return nil
    99  			} else if !retry {
   100  				return c.Error
   101  			}
   102  		case <-c.Interrupter:
   103  			return ErrInterrupted
   104  		}
   105  	}
   106  }