go.uber.org/cadence@v1.2.9/internal/common/backoff/retrypolicy_test.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package backoff 22 23 import ( 24 "testing" 25 "time" 26 27 "github.com/stretchr/testify/assert" 28 ) 29 30 type TestClock struct { 31 currentTime time.Time 32 } 33 34 func TestExponentialBackoff(t *testing.T) { 35 t.Parallel() 36 policy := createPolicy(time.Second) 37 policy.SetMaximumInterval(10 * time.Second) 38 39 expectedResult := []time.Duration{1, 2, 4, 8, 10} 40 for i, d := range expectedResult { 41 expectedResult[i] = d * time.Second 42 } 43 44 r, _ := createRetrier(policy) 45 for _, expected := range expectedResult { 46 min, max := getNextBackoffRange(expected) 47 next := r.NextBackOff() 48 assert.True(t, next >= min, "NextBackoff too low") 49 assert.True(t, next < max, "NextBackoff too high") 50 } 51 } 52 53 func TestNumberOfAttempts(t *testing.T) { 54 t.Parallel() 55 policy := createPolicy(time.Second) 56 policy.SetMaximumAttempts(5) 57 58 r, _ := createRetrier(policy) 59 var next time.Duration 60 for i := 0; i < 6; i++ { 61 next = r.NextBackOff() 62 } 63 64 assert.Equal(t, done, next) 65 } 66 67 // Test to make sure relative maximum interval for each retry is honoured 68 func TestMaximumInterval(t *testing.T) { 69 t.Parallel() 70 policy := createPolicy(time.Second) 71 policy.SetMaximumInterval(10 * time.Second) 72 73 expectedResult := []time.Duration{1, 2, 4, 8, 10, 10, 10, 10, 10, 10} 74 for i, d := range expectedResult { 75 expectedResult[i] = d * time.Second 76 } 77 78 r, _ := createRetrier(policy) 79 for _, expected := range expectedResult { 80 min, max := getNextBackoffRange(expected) 81 next := r.NextBackOff() 82 assert.True(t, next >= min, "NextBackoff too low") 83 assert.True(t, next < max, "NextBackoff too high") 84 } 85 } 86 87 func TestBackoffCoefficient(t *testing.T) { 88 t.Parallel() 89 policy := createPolicy(2 * time.Second) 90 policy.SetBackoffCoefficient(1.0) 91 92 r, _ := createRetrier(policy) 93 min, max := getNextBackoffRange(2 * time.Second) 94 for i := 0; i < 10; i++ { 95 next := r.NextBackOff() 96 assert.True(t, next >= min, "NextBackoff too low") 97 assert.True(t, next < max, "NextBackoff too high") 98 } 99 } 100 101 func TestExpirationInterval(t *testing.T) { 102 t.Parallel() 103 policy := createPolicy(2 * time.Second) 104 policy.SetExpirationInterval(5 * time.Minute) 105 106 r, clock := createRetrier(policy) 107 clock.moveClock(6 * time.Minute) 108 next := r.NextBackOff() 109 110 assert.Equal(t, done, next) 111 } 112 113 func TestExpirationOverflow(t *testing.T) { 114 t.Parallel() 115 policy := createPolicy(2 * time.Second) 116 policy.SetExpirationInterval(5 * time.Second) 117 118 r, clock := createRetrier(policy) 119 next := r.NextBackOff() 120 min, max := getNextBackoffRange(2 * time.Second) 121 assert.True(t, next >= min, "NextBackoff too low") 122 assert.True(t, next < max, "NextBackoff too high") 123 124 clock.moveClock(2 * time.Second) 125 126 next = r.NextBackOff() 127 min, max = getNextBackoffRange(3 * time.Second) 128 assert.True(t, next >= min, "NextBackoff too low") 129 assert.True(t, next < max, "NextBackoff too high") 130 } 131 132 func TestDefaultPublishRetryPolicy(t *testing.T) { 133 t.Parallel() 134 policy := NewExponentialRetryPolicy(50 * time.Millisecond) 135 policy.SetExpirationInterval(time.Minute) 136 policy.SetMaximumInterval(10 * time.Second) 137 138 r, clock := createRetrier(policy) 139 expectedResult := []time.Duration{ 140 50 * time.Millisecond, 141 100 * time.Millisecond, 142 200 * time.Millisecond, 143 400 * time.Millisecond, 144 800 * time.Millisecond, 145 1600 * time.Millisecond, 146 3200 * time.Millisecond, 147 6400 * time.Millisecond, 148 10000 * time.Millisecond, 149 10000 * time.Millisecond, 150 10000 * time.Millisecond, 151 10000 * time.Millisecond, 152 7250 * time.Millisecond, 153 done, 154 } 155 156 for _, expected := range expectedResult { 157 next := r.NextBackOff() 158 if expected == done { 159 assert.Equal(t, done, next, "backoff not done yet!!!") 160 } else { 161 min, _ := getNextBackoffRange(expected) 162 assert.True(t, next >= min, "NextBackoff too low: actual: %v, expected: %v", next, expected) 163 // s.True(next < max, "NextBackoff too high: actual: %v, expected: %v", next, expected) 164 clock.moveClock(expected) 165 } 166 } 167 } 168 169 func TestNoMaxAttempts(t *testing.T) { 170 t.Parallel() 171 policy := createPolicy(50 * time.Millisecond) 172 policy.SetExpirationInterval(time.Minute) 173 policy.SetMaximumInterval(10 * time.Second) 174 175 r, clock := createRetrier(policy) 176 for i := 0; i < 100; i++ { 177 next := r.NextBackOff() 178 assert.True(t, next > 0 || next == done, "Unexpected value for next retry duration: %v", next) 179 clock.moveClock(next) 180 } 181 } 182 183 func TestUnbounded(t *testing.T) { 184 t.Parallel() 185 policy := createPolicy(50 * time.Millisecond) 186 187 r, clock := createRetrier(policy) 188 for i := 0; i < 100; i++ { 189 next := r.NextBackOff() 190 assert.True(t, next > 0 || next == done, "Unexpected value for next retry duration: %v", next) 191 clock.moveClock(next) 192 } 193 } 194 195 func (c *TestClock) Now() time.Time { 196 return c.currentTime 197 } 198 199 func (c *TestClock) moveClock(duration time.Duration) { 200 c.currentTime = c.currentTime.Add(duration) 201 } 202 203 func createPolicy(initialInterval time.Duration) *ExponentialRetryPolicy { 204 policy := NewExponentialRetryPolicy(initialInterval) 205 policy.SetBackoffCoefficient(2) 206 policy.SetMaximumInterval(NoInterval) 207 policy.SetExpirationInterval(NoInterval) 208 policy.SetMaximumAttempts(noMaximumAttempts) 209 210 return policy 211 } 212 213 func createRetrier(policy RetryPolicy) (Retrier, *TestClock) { 214 clock := &TestClock{currentTime: time.Time{}} 215 return NewRetrier(policy, clock), clock 216 } 217 218 func getNextBackoffRange(duration time.Duration) (time.Duration, time.Duration) { 219 rangeMin := time.Duration(0.8 * float64(duration)) 220 return rangeMin, duration 221 }