|
@@ -2,6 +2,7 @@
|
|
|
package retrier
|
|
package retrier
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
|
+ "context"
|
|
|
"math/rand"
|
|
"math/rand"
|
|
|
"sync"
|
|
"sync"
|
|
|
"time"
|
|
"time"
|
|
@@ -33,15 +34,23 @@ func New(backoff []time.Duration, class Classifier) *Retrier {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Run executes the given work function, then classifies its return value based on the classifier used
|
|
|
|
|
|
|
+// Run executes the given work function by executing RunCtx without context.Context.
|
|
|
|
|
+func (r *Retrier) Run(work func() error) error {
|
|
|
|
|
+ return r.RunCtx(context.Background(), func(ctx context.Context) error {
|
|
|
|
|
+ // never use ctx
|
|
|
|
|
+ return work()
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// RunCtx executes the given work function, then classifies its return value based on the classifier used
|
|
|
// to construct the Retrier. If the result is Succeed or Fail, the return value of the work function is
|
|
// to construct the Retrier. If the result is Succeed or Fail, the return value of the work function is
|
|
|
// returned to the caller. If the result is Retry, then Run sleeps according to the its backoff policy
|
|
// returned to the caller. If the result is Retry, then Run sleeps according to the its backoff policy
|
|
|
// before retrying. If the total number of retries is exceeded then the return value of the work function
|
|
// before retrying. If the total number of retries is exceeded then the return value of the work function
|
|
|
// is returned to the caller regardless.
|
|
// is returned to the caller regardless.
|
|
|
-func (r *Retrier) Run(work func() error) error {
|
|
|
|
|
|
|
+func (r *Retrier) RunCtx(ctx context.Context, work func(ctx context.Context) error) error {
|
|
|
retries := 0
|
|
retries := 0
|
|
|
for {
|
|
for {
|
|
|
- ret := work()
|
|
|
|
|
|
|
+ ret := work(ctx)
|
|
|
|
|
|
|
|
switch r.class.Classify(ret) {
|
|
switch r.class.Classify(ret) {
|
|
|
case Succeed, Fail:
|
|
case Succeed, Fail:
|
|
@@ -50,12 +59,26 @@ func (r *Retrier) Run(work func() error) error {
|
|
|
if retries >= len(r.backoff) {
|
|
if retries >= len(r.backoff) {
|
|
|
return ret
|
|
return ret
|
|
|
}
|
|
}
|
|
|
- time.Sleep(r.calcSleep(retries))
|
|
|
|
|
|
|
+
|
|
|
|
|
+ timeout := time.After(r.calcSleep(retries))
|
|
|
|
|
+ if err := r.sleep(ctx, timeout); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
retries++
|
|
retries++
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func (r *Retrier) sleep(ctx context.Context, t <-chan time.Time) error {
|
|
|
|
|
+ select {
|
|
|
|
|
+ case <-t:
|
|
|
|
|
+ return nil
|
|
|
|
|
+ case <-ctx.Done():
|
|
|
|
|
+ return ctx.Err()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func (r *Retrier) calcSleep(i int) time.Duration {
|
|
func (r *Retrier) calcSleep(i int) time.Duration {
|
|
|
// lock unsafe rand prng
|
|
// lock unsafe rand prng
|
|
|
r.randMu.Lock()
|
|
r.randMu.Lock()
|