github.com/bytedance/gopkg@v0.0.0-20240514070511-01b2cbcf35e1/cloud/circuitbreaker/breaker_test.go (about) 1 // Copyright 2021 ByteDance Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package circuitbreaker 16 17 import ( 18 "math/rand" 19 "sync" 20 "sync/atomic" 21 "testing" 22 "time" 23 ) 24 25 func BenchmarkBreaker(b *testing.B) { 26 op := Options{ 27 ShouldTrip: ConsecutiveTripFunc(1000), 28 } 29 cb, _ := newBreaker(op) 30 31 b.ResetTimer() 32 for i := 0; i < b.N; i++ { 33 cb.IsAllowed() 34 cb.Succeed() 35 } 36 } 37 38 func BenchmarkBreakerParallel(b *testing.B) { 39 op := Options{ 40 ShouldTrip: ConsecutiveTripFunc(1000), 41 } 42 cb, _ := newBreaker(op) 43 44 b.ResetTimer() 45 b.RunParallel(func(pb *testing.PB) { 46 for pb.Next() { 47 cb.IsAllowed() 48 cb.Succeed() 49 } 50 }) 51 } 52 53 func BenchmarkBreakerParallel2Cores(b *testing.B) { 54 op := Options{ 55 ShouldTrip: ConsecutiveTripFunc(1000), 56 } 57 cb, _ := newBreaker(op) 58 b.SetParallelism(2) 59 60 b.ResetTimer() 61 b.RunParallel(func(pb *testing.PB) { 62 for pb.Next() { 63 cb.IsAllowed() 64 cb.Succeed() 65 } 66 }) 67 } 68 69 func TestBreakerConsecutiveTrip(t *testing.T) { 70 cooling := time.Millisecond * 50 71 retry := time.Millisecond * 20 72 73 op := Options{ 74 CoolingTimeout: cooling, 75 DetectTimeout: retry, 76 ShouldTrip: ConsecutiveTripFunc(1000), 77 } 78 cb, _ := newBreaker(op) 79 80 for i := 0; i < 999; i++ { 81 assert(t, cb.IsAllowed()) 82 cb.Fail() 83 } 84 85 assert(t, cb.IsAllowed()) 86 cb.Fail() 87 assert(t, !cb.IsAllowed()) 88 89 time.Sleep(cooling) 90 assert(t, cb.IsAllowed()) 91 cb.Fail() 92 assert(t, !cb.IsAllowed()) 93 94 time.Sleep(cooling) 95 assert(t, cb.IsAllowed()) 96 cb.Succeed() 97 98 assert(t, !cb.IsAllowed()) 99 assert(t, cb.State() == HalfOpen) 100 101 time.Sleep(retry) 102 assert(t, cb.IsAllowed()) 103 cb.Succeed() 104 assert(t, cb.State() == Closed) 105 106 for i := 0; i < 100; i++ { 107 assert(t, cb.IsAllowed()) 108 cb.Timeout() 109 } 110 111 assert(t, cb.IsAllowed()) 112 cb.Succeed() 113 114 for i := 0; i < 1000; i++ { 115 assert(t, cb.IsAllowed()) 116 cb.Timeout() 117 } 118 119 assert(t, !cb.IsAllowed()) 120 } 121 122 func TestBreakerThresholdTrip(t *testing.T) { 123 cooling := time.Millisecond * 50 124 retry := time.Millisecond * 20 125 126 op := Options{ 127 CoolingTimeout: cooling, 128 DetectTimeout: retry, 129 ShouldTrip: ThresholdTripFunc(1000), 130 } 131 132 cb, _ := newBreaker(op) 133 134 for i := 0; i < 999; i++ { 135 assert(t, cb.IsAllowed()) 136 cb.Timeout() 137 } 138 139 for i := 0; i < 1000; i++ { 140 assert(t, cb.IsAllowed()) 141 cb.Succeed() 142 } 143 144 assert(t, cb.IsAllowed()) 145 cb.Fail() 146 147 assert(t, !cb.IsAllowed()) 148 } 149 150 func TestBreakerInstanceTrip(t *testing.T) { 151 op := Options{ 152 ShouldTrip: ConsecutiveTripFuncV2(0.5, 1000, 3*time.Second, 50, 500), 153 } 154 155 cb, _ := newBreaker(op) 156 157 for i := 0; i < 100; i++ { 158 assert(t, cb.IsAllowed()) 159 cb.Timeout() 160 } 161 time.Sleep(3 * time.Second) 162 assert(t, cb.IsAllowed()) 163 cb.Timeout() 164 165 for i := 0; i < 100; i++ { 166 assert(t, !cb.IsAllowed()) 167 } 168 } 169 170 func TestBreakerRateTrip(t *testing.T) { 171 cooling := time.Millisecond * 50 172 retry := time.Millisecond * 20 173 174 op := Options{ 175 CoolingTimeout: cooling, 176 DetectTimeout: retry, 177 ShouldTrip: RateTripFunc(.5, 1000), 178 } 179 180 cb, _ := newBreaker(op) 181 182 for i := 0; i < 499; i++ { 183 assert(t, cb.IsAllowed()) 184 cb.Timeout() 185 } 186 187 for i := 0; i < 500; i++ { 188 assert(t, cb.IsAllowed()) 189 cb.Succeed() 190 } 191 192 assert(t, cb.IsAllowed()) 193 cb.Fail() 194 assert(t, !cb.IsAllowed()) 195 assert(t, cb.metricer.ErrorRate() == .5) 196 197 time.Sleep(cooling) 198 assert(t, cb.IsAllowed()) 199 cb.Succeed() 200 201 assert(t, cb.State() == HalfOpen) 202 } 203 204 func TestBreakerRateTrip2(t *testing.T) { 205 cooling := time.Millisecond 206 retry := time.Millisecond / 10 207 208 op := Options{ 209 CoolingTimeout: cooling, 210 DetectTimeout: retry, 211 ShouldTrip: RateTripFunc(.5, 1000), 212 } 213 214 cb, _ := newBreaker(op) 215 216 for i := 0; i < 1000000; i++ { 217 if cb.IsAllowed() == false { 218 time.Sleep(retry) 219 continue 220 } 221 222 r := rand.Intn(100) 223 if r < 60 { // 60% fail 224 cb.Fail() 225 } else { 226 cb.Succeed() 227 } 228 } 229 230 if cb.metricer.Samples() > 1000 && cb.metricer.ErrorRate() >= .5 { 231 assert(t, cb.State() != Closed) 232 233 } else { 234 assert(t, cb.State() == Closed) 235 } 236 } 237 238 func TestBreakerReset(t *testing.T) { 239 cooling := time.Millisecond 240 retry := time.Millisecond / 10 241 242 op := Options{ 243 CoolingTimeout: cooling, 244 DetectTimeout: retry, 245 ShouldTrip: RateTripFunc(.5, 1000), 246 } 247 248 cb, _ := newBreaker(op) 249 250 for i := 0; i < 1000; i++ { 251 assert(t, cb.IsAllowed()) 252 cb.Timeout() 253 } 254 255 assert(t, cb.metricer.ErrorRate() == 1) 256 assert(t, cb.metricer.Samples() == 1000) 257 assert(t, cb.metricer.Timeouts() == 1000) 258 assert(t, !cb.IsAllowed()) 259 260 cb.Reset() 261 262 assert(t, cb.metricer.ErrorRate() == 0) 263 assert(t, cb.metricer.Samples() == 0) 264 assert(t, cb.metricer.Timeouts() == 0) 265 assert(t, cb.IsAllowed()) 266 } 267 268 func TestBreakerConcurrent(t *testing.T) { 269 cooling := time.Millisecond * 100 270 retry := time.Millisecond * 50 271 opt := Options{ 272 CoolingTimeout: cooling, 273 DetectTimeout: retry, 274 ShouldTrip: RateTripFunc(0.5, 2), 275 } 276 var w sync.WaitGroup 277 for i := 0; i < 10; i++ { 278 w.Add(1) 279 go func() { 280 defer w.Done() 281 b, _ := newBreaker(opt) 282 // close -> open 283 for i := 0; i < 2; i++ { 284 b.Fail() 285 } 286 if b.State() != Open { 287 t.Errorf("want open state but got %s", b.State()) 288 } 289 290 // CoolingTimeout 291 time.Sleep(cooling) 292 var wg sync.WaitGroup 293 pass := int32(0) 294 fail := int32(0) 295 for i := 0; i < 50; i++ { 296 wg.Add(1) 297 go func() { 298 defer wg.Done() 299 if b.IsAllowed() { 300 atomic.AddInt32(&pass, 1) 301 } else { 302 atomic.AddInt32(&fail, 1) 303 } 304 }() 305 } 306 wg.Wait() 307 if pass != 1 { 308 t.Errorf("want 1 pass but got %d pass", pass) 309 } 310 if fail != 49 { 311 t.Errorf("want 49 fails but got %d fails", fail) 312 } 313 }() 314 } 315 w.Wait() 316 }