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 }