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