blob: 3becab216cefc5f2d01384a88be0bef2deef360d [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001// Copyright 2011, 2012, 2013 Canonical Ltd.
2// Licensed under the LGPLv3, see LICENCE file for details.
3
4package utils
5
6import (
7 "time"
8)
9
10// The Attempt and AttemptStrategy types are copied from those in launchpad.net/goamz/aws.
11
12// AttemptStrategy represents a strategy for waiting for an action
13// to complete successfully.
14type AttemptStrategy struct {
15 Total time.Duration // total duration of attempt.
16 Delay time.Duration // interval between each try in the burst.
17 Min int // minimum number of retries; overrides Total
18}
19
20type Attempt struct {
21 strategy AttemptStrategy
22 last time.Time
23 end time.Time
24 force bool
25 count int
26}
27
28// Start begins a new sequence of attempts for the given strategy.
29func (s AttemptStrategy) Start() *Attempt {
30 now := time.Now()
31 return &Attempt{
32 strategy: s,
33 last: now,
34 end: now.Add(s.Total),
35 force: true,
36 }
37}
38
39// Next waits until it is time to perform the next attempt or returns
40// false if it is time to stop trying.
41// It always returns true the first time it is called - we are guaranteed to
42// make at least one attempt.
43func (a *Attempt) Next() bool {
44 now := time.Now()
45 sleep := a.nextSleep(now)
46 if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count {
47 return false
48 }
49 a.force = false
50 if sleep > 0 && a.count > 0 {
51 time.Sleep(sleep)
52 now = time.Now()
53 }
54 a.count++
55 a.last = now
56 return true
57}
58
59func (a *Attempt) nextSleep(now time.Time) time.Duration {
60 sleep := a.strategy.Delay - now.Sub(a.last)
61 if sleep < 0 {
62 return 0
63 }
64 return sleep
65}
66
67// HasNext returns whether another attempt will be made if the current
68// one fails. If it returns true, the following call to Next is
69// guaranteed to return true.
70func (a *Attempt) HasNext() bool {
71 if a.force || a.strategy.Min > a.count {
72 return true
73 }
74 now := time.Now()
75 if now.Add(a.nextSleep(now)).Before(a.end) {
76 a.force = true
77 return true
78 }
79 return false
80}