github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/s3/attempt.go (about)

     1  package s3
     2  
     3  import (
     4  	"time"
     5  )
     6  
     7  // AttemptStrategy represents a strategy for waiting for an action
     8  // to complete successfully. This is an internal type used by the
     9  // implementation of other goamz packages.
    10  type AttemptStrategy struct {
    11  	Total time.Duration // total duration of attempt.
    12  	Delay time.Duration // interval between each try in the burst.
    13  	Min   int           // minimum number of retries; overrides Total
    14  }
    15  
    16  type Attempt struct {
    17  	strategy AttemptStrategy
    18  	last     time.Time
    19  	end      time.Time
    20  	force    bool
    21  	count    int
    22  }
    23  
    24  // Start begins a new sequence of attempts for the given strategy.
    25  func (s AttemptStrategy) Start() *Attempt {
    26  	now := time.Now()
    27  	return &Attempt{
    28  		strategy: s,
    29  		last:     now,
    30  		end:      now.Add(s.Total),
    31  		force:    true,
    32  	}
    33  }
    34  
    35  // Next waits until it is time to perform the next attempt or returns
    36  // false if it is time to stop trying.
    37  func (a *Attempt) Next() bool {
    38  	now := time.Now()
    39  	sleep := a.nextSleep(now)
    40  	if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count {
    41  		return false
    42  	}
    43  	a.force = false
    44  	if sleep > 0 && a.count > 0 {
    45  		time.Sleep(sleep)
    46  		now = time.Now()
    47  	}
    48  	a.count++
    49  	a.last = now
    50  	return true
    51  }
    52  
    53  func (a *Attempt) nextSleep(now time.Time) time.Duration {
    54  	sleep := a.strategy.Delay - now.Sub(a.last)
    55  	if sleep < 0 {
    56  		return 0
    57  	}
    58  	return sleep
    59  }
    60  
    61  // HasNext returns whether another attempt will be made if the current
    62  // one fails. If it returns true, the following call to Next is
    63  // guaranteed to return true.
    64  func (a *Attempt) HasNext() bool {
    65  	if a.force || a.strategy.Min > a.count {
    66  		return true
    67  	}
    68  	now := time.Now()
    69  	if now.Add(a.nextSleep(now)).Before(a.end) {
    70  		a.force = true
    71  		return true
    72  	}
    73  	return false
    74  }