1package expb
2
3import (
4	"fmt"
5	"math/rand"
6	"time"
7)
8
9type StatusChecker func(q interface{}) (ok, retryable bool)
10
11type ExponentialBacker struct {
12	Debug       bool
13	Do          Producer
14	RetryCount  uint32
15	StatusCheck StatusChecker
16}
17
18type pair struct {
19	first interface{}
20	last  interface{}
21}
22
23type Callback func(result interface{}, err error)
24type Producer func() (result interface{}, err error)
25
26func ExponentialBackOffSync(bk *ExponentialBacker) (interface{}, error) {
27
28	done := make(chan *pair)
29
30	go func() {
31		defer close(done)
32		retries := uint32(0)
33		for {
34			res, err := bk.Do()
35			ok, retryable := bk.StatusCheck(res)
36
37			if ok || !retryable || retries >= bk.RetryCount {
38				done <- &pair{first: res, last: err}
39				break
40			}
41
42			ms := time.Duration(1e9*rand.Float64()) + ((1 << retries) * time.Second)
43			if bk.Debug {
44				fmt.Printf("trying again in %v\n", ms)
45			}
46
47			duration := time.Duration(ms)
48			time.Sleep(duration)
49
50			retries += 1
51		}
52	}()
53
54	v := <-done
55
56	res := v.first
57	err, _ := v.last.(error)
58
59	return res, err
60}
61
62func ExponentialBackOff(bk *ExponentialBacker, cb Callback) {
63	res, err := ExponentialBackOffSync(bk)
64	cb(res, err)
65}
66