Codebase list golang-gopkg-eapache-go-resiliency.v1 / f158e2e
Update upstream source from tag 'upstream/1.2.0' Update to upstream version '1.2.0' with Debian dir 064e7033bb7d58b4dedd1307d3a1c929444f9602 Anthony Fok 2 years ago
12 changed file(s) with 141 addition(s) and 11 deletion(s). Raw diff Collapse all Expand all
00 language: go
11
22 go:
3 - 1.1
4 - 1.2
5 - 1.3
6 - 1.4
3 - 1.7
4 - 1.12
0 # Changelog
1
2 #### Version 1.2.0 (2019-06-14)
3
4 *Note: This release requires Golang at least 1.7, which is higher than the
5 previous release. All the versions being dropped are multiple years old and no
6 longer supported upstream, so I'm not counting this as a breaking change.*
7
8 - Add `RunCtx` method on `Retrier` to support running with a context.
9 - Ensure the `Retrier`'s use of random numbers is concurrency-safe.
10 - Bump CI to ensure we support newer Golang versions.
11
12 #### Version 1.1.0 (2018-03-26)
13
14 - Improve documentation and fix some typos.
15 - Bump CI to ensure we support newer Golang versions.
16 - Add `IsEmpty()` method on `Semaphore`.
17
18 #### Version 1.0.0 (2015-02-13)
19
20 Initial release.
22
33 [![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency)
44 [![GoDoc](https://godoc.org/github.com/eapache/go-resiliency?status.svg)](https://godoc.org/github.com/eapache/go-resiliency)
5 [![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html)
56
67 Resiliency patterns for golang.
78 Based in part on [Hystrix](https://github.com/Netflix/Hystrix),
22
33 [![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency)
44 [![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/batcher?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/batcher)
5 [![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html)
56
67 The batching resiliency pattern for golang.
78
22
33 [![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency)
44 [![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/breaker?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/breaker)
5 [![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html)
56
67 The circuit-breaker resiliency pattern for golang.
78
22
33 [![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency)
44 [![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/deadline?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/deadline)
5 [![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html)
56
67 The deadline/timeout resiliency pattern for golang.
78
1112 dl := deadline.New(1 * time.Second)
1213
1314 err := dl.Run(func(stopper <-chan struct{}) error {
14 // do something possibly slow
15 // check stopper function and give up if timed out
15 // do something potentially slow
16 // give up when the `stopper` channel is closed (indicating a time-out)
1617 return nil
1718 })
1819
22
33 [![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency)
44 [![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/retrier?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/retrier)
5 [![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html)
56
67 The retriable resiliency pattern for golang.
78
11 package retrier
22
33 import (
4 "context"
45 "math/rand"
6 "sync"
57 "time"
68 )
79
1214 class Classifier
1315 jitter float64
1416 rand *rand.Rand
17 randMu sync.Mutex
1518 }
1619
1720 // New constructs a Retrier with the given backoff pattern and classifier. The length of the backoff pattern
3033 }
3134 }
3235
33 // Run executes the given work function, then classifies its return value based on the classifier used
36 // Run executes the given work function by executing RunCtx without context.Context.
37 func (r *Retrier) Run(work func() error) error {
38 return r.RunCtx(context.Background(), func(ctx context.Context) error {
39 // never use ctx
40 return work()
41 })
42 }
43
44 // RunCtx executes the given work function, then classifies its return value based on the classifier used
3445 // to construct the Retrier. If the result is Succeed or Fail, the return value of the work function is
3546 // returned to the caller. If the result is Retry, then Run sleeps according to the its backoff policy
3647 // before retrying. If the total number of retries is exceeded then the return value of the work function
3748 // is returned to the caller regardless.
38 func (r *Retrier) Run(work func() error) error {
49 func (r *Retrier) RunCtx(ctx context.Context, work func(ctx context.Context) error) error {
3950 retries := 0
4051 for {
41 ret := work()
52 ret := work(ctx)
4253
4354 switch r.class.Classify(ret) {
4455 case Succeed, Fail:
4758 if retries >= len(r.backoff) {
4859 return ret
4960 }
50 time.Sleep(r.calcSleep(retries))
61
62 timeout := time.After(r.calcSleep(retries))
63 if err := r.sleep(ctx, timeout); err != nil {
64 return err
65 }
66
5167 retries++
5268 }
5369 }
5470 }
5571
72 func (r *Retrier) sleep(ctx context.Context, t <-chan time.Time) error {
73 select {
74 case <-t:
75 return nil
76 case <-ctx.Done():
77 return ctx.Err()
78 }
79 }
80
5681 func (r *Retrier) calcSleep(i int) time.Duration {
82 // lock unsafe rand prng
83 r.randMu.Lock()
84 defer r.randMu.Unlock()
5785 // take a random float in the range (-r.jitter, +r.jitter) and multiply it by the base amount
5886 return r.backoff[i] + time.Duration(((r.rand.Float64()*2)-1)*r.jitter*float64(r.backoff[i]))
5987 }
00 package retrier
11
22 import (
3 "context"
4 "errors"
35 "testing"
46 "time"
57 )
1416 return nil
1517 }
1618 return returns[i-1]
19 }
20 }
21
22 func genWorkWithCtx() func(ctx context.Context) error {
23 i = 0
24 return func(ctx context.Context) error {
25 select {
26 case <-ctx.Done():
27 return errFoo
28 default:
29 i++
30 }
31 return nil
1732 }
1833 }
1934
4156 t.Error(err)
4257 }
4358 if i != 1 {
59 t.Error("run wrong number of times")
60 }
61 }
62
63 func TestRetrierCtx(t *testing.T) {
64 ctx, cancel := context.WithCancel(context.Background())
65
66 r := New([]time.Duration{0, 10 * time.Millisecond}, WhitelistClassifier{})
67
68 err := r.RunCtx(ctx, genWorkWithCtx())
69 if err != nil {
70 t.Error(err)
71 }
72 if i != 1 {
73 t.Error("run wrong number of times")
74 }
75
76 cancel()
77
78 err = r.RunCtx(ctx, genWorkWithCtx())
79 if err != errFoo {
80 t.Error("context must be cancelled")
81 }
82 if i != 0 {
4483 t.Error("run wrong number of times")
4584 }
4685 }
114153 }
115154 }
116155
156 func TestRetrierThreadSafety(t *testing.T) {
157 r := New([]time.Duration{0}, nil)
158 for i := 0; i < 2; i++ {
159 go func() {
160 r.Run(func() error {
161 return errors.New("error")
162 })
163 }()
164 }
165 }
166
117167 func ExampleRetrier() {
118168 r := New(ConstantBackoff(3, 100*time.Millisecond), nil)
119169
22
33 [![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency)
44 [![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/semaphore?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/semaphore)
5 [![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html)
56
67 The semaphore resiliency pattern for golang.
78
77
88 // ErrNoTickets is the error returned by Acquire when it could not acquire
99 // a ticket from the semaphore within the configured timeout.
10 var ErrNoTickets = errors.New("could not aquire semaphore ticket")
10 var ErrNoTickets = errors.New("could not acquire semaphore ticket")
1111
1212 // Semaphore implements the semaphore resiliency pattern
1313 type Semaphore struct {
4242 func (s *Semaphore) Release() {
4343 <-s.sem
4444 }
45
46 // IsEmpty will return true if no tickets are being held at that instant.
47 // It is safe to call concurrently with Acquire and Release, though do note
48 // that the result may then be unpredictable.
49 func (s *Semaphore) IsEmpty() bool {
50 return len(s.sem) == 0
51 }
4444 }
4545 }
4646
47 func TestSemaphoreEmpty(t *testing.T) {
48 sem := New(2, 200*time.Millisecond)
49
50 if !sem.IsEmpty() {
51 t.Error("semaphore should be empty")
52 }
53
54 sem.Acquire()
55
56 if sem.IsEmpty() {
57 t.Error("semaphore should not be empty")
58 }
59
60 sem.Release()
61
62 if !sem.IsEmpty() {
63 t.Error("semaphore should be empty")
64 }
65 }
66
4767 func ExampleSemaphore() {
4868 sem := New(3, 1*time.Second)
4969