github.com/lingyao2333/mo-zero@v1.4.1/core/breaker/googlebreaker_test.go (about) 1 package breaker 2 3 import ( 4 "errors" 5 "math/rand" 6 "testing" 7 "time" 8 9 "github.com/lingyao2333/mo-zero/core/collection" 10 "github.com/lingyao2333/mo-zero/core/mathx" 11 "github.com/lingyao2333/mo-zero/core/stat" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 const ( 16 testBuckets = 10 17 testInterval = time.Millisecond * 10 18 ) 19 20 func init() { 21 stat.SetReporter(nil) 22 } 23 24 func getGoogleBreaker() *googleBreaker { 25 st := collection.NewRollingWindow(testBuckets, testInterval) 26 return &googleBreaker{ 27 stat: st, 28 k: 5, 29 proba: mathx.NewProba(), 30 } 31 } 32 33 func markSuccessWithDuration(b *googleBreaker, count int, sleep time.Duration) { 34 for i := 0; i < count; i++ { 35 b.markSuccess() 36 time.Sleep(sleep) 37 } 38 } 39 40 func markFailedWithDuration(b *googleBreaker, count int, sleep time.Duration) { 41 for i := 0; i < count; i++ { 42 b.markFailure() 43 time.Sleep(sleep) 44 } 45 } 46 47 func TestGoogleBreakerClose(t *testing.T) { 48 b := getGoogleBreaker() 49 markSuccess(b, 80) 50 assert.Nil(t, b.accept()) 51 markSuccess(b, 120) 52 assert.Nil(t, b.accept()) 53 } 54 55 func TestGoogleBreakerOpen(t *testing.T) { 56 b := getGoogleBreaker() 57 markSuccess(b, 10) 58 assert.Nil(t, b.accept()) 59 markFailed(b, 100000) 60 time.Sleep(testInterval * 2) 61 verify(t, func() bool { 62 return b.accept() != nil 63 }) 64 } 65 66 func TestGoogleBreakerFallback(t *testing.T) { 67 b := getGoogleBreaker() 68 markSuccess(b, 1) 69 assert.Nil(t, b.accept()) 70 markFailed(b, 10000) 71 time.Sleep(testInterval * 2) 72 verify(t, func() bool { 73 return b.doReq(func() error { 74 return errors.New("any") 75 }, func(err error) error { 76 return nil 77 }, defaultAcceptable) == nil 78 }) 79 } 80 81 func TestGoogleBreakerReject(t *testing.T) { 82 b := getGoogleBreaker() 83 markSuccess(b, 100) 84 assert.Nil(t, b.accept()) 85 markFailed(b, 10000) 86 time.Sleep(testInterval) 87 assert.Equal(t, ErrServiceUnavailable, b.doReq(func() error { 88 return ErrServiceUnavailable 89 }, nil, defaultAcceptable)) 90 } 91 92 func TestGoogleBreakerAcceptable(t *testing.T) { 93 b := getGoogleBreaker() 94 errAcceptable := errors.New("any") 95 assert.Equal(t, errAcceptable, b.doReq(func() error { 96 return errAcceptable 97 }, nil, func(err error) bool { 98 return err == errAcceptable 99 })) 100 } 101 102 func TestGoogleBreakerNotAcceptable(t *testing.T) { 103 b := getGoogleBreaker() 104 errAcceptable := errors.New("any") 105 assert.Equal(t, errAcceptable, b.doReq(func() error { 106 return errAcceptable 107 }, nil, func(err error) bool { 108 return err != errAcceptable 109 })) 110 } 111 112 func TestGoogleBreakerPanic(t *testing.T) { 113 b := getGoogleBreaker() 114 assert.Panics(t, func() { 115 _ = b.doReq(func() error { 116 panic("fail") 117 }, nil, defaultAcceptable) 118 }) 119 } 120 121 func TestGoogleBreakerHalfOpen(t *testing.T) { 122 b := getGoogleBreaker() 123 assert.Nil(t, b.accept()) 124 t.Run("accept single failed/accept", func(t *testing.T) { 125 markFailed(b, 10000) 126 time.Sleep(testInterval * 2) 127 verify(t, func() bool { 128 return b.accept() != nil 129 }) 130 }) 131 t.Run("accept single failed/allow", func(t *testing.T) { 132 markFailed(b, 10000) 133 time.Sleep(testInterval * 2) 134 verify(t, func() bool { 135 _, err := b.allow() 136 return err != nil 137 }) 138 }) 139 time.Sleep(testInterval * testBuckets) 140 t.Run("accept single succeed", func(t *testing.T) { 141 assert.Nil(t, b.accept()) 142 markSuccess(b, 10000) 143 verify(t, func() bool { 144 return b.accept() == nil 145 }) 146 }) 147 } 148 149 func TestGoogleBreakerSelfProtection(t *testing.T) { 150 t.Run("total request < 100", func(t *testing.T) { 151 b := getGoogleBreaker() 152 markFailed(b, 4) 153 time.Sleep(testInterval) 154 assert.Nil(t, b.accept()) 155 }) 156 t.Run("total request > 100, total < 2 * success", func(t *testing.T) { 157 b := getGoogleBreaker() 158 size := rand.Intn(10000) 159 accepts := size + 1 160 markSuccess(b, accepts) 161 markFailed(b, size-accepts) 162 assert.Nil(t, b.accept()) 163 }) 164 } 165 166 func TestGoogleBreakerHistory(t *testing.T) { 167 var b *googleBreaker 168 var accepts, total int64 169 170 sleep := testInterval 171 t.Run("accepts == total", func(t *testing.T) { 172 b = getGoogleBreaker() 173 markSuccessWithDuration(b, 10, sleep/2) 174 accepts, total = b.history() 175 assert.Equal(t, int64(10), accepts) 176 assert.Equal(t, int64(10), total) 177 }) 178 179 t.Run("fail == total", func(t *testing.T) { 180 b = getGoogleBreaker() 181 markFailedWithDuration(b, 10, sleep/2) 182 accepts, total = b.history() 183 assert.Equal(t, int64(0), accepts) 184 assert.Equal(t, int64(10), total) 185 }) 186 187 t.Run("accepts = 1/2 * total, fail = 1/2 * total", func(t *testing.T) { 188 b = getGoogleBreaker() 189 markFailedWithDuration(b, 5, sleep/2) 190 markSuccessWithDuration(b, 5, sleep/2) 191 accepts, total = b.history() 192 assert.Equal(t, int64(5), accepts) 193 assert.Equal(t, int64(10), total) 194 }) 195 196 t.Run("auto reset rolling counter", func(t *testing.T) { 197 b = getGoogleBreaker() 198 time.Sleep(testInterval * testBuckets) 199 accepts, total = b.history() 200 assert.Equal(t, int64(0), accepts) 201 assert.Equal(t, int64(0), total) 202 }) 203 } 204 205 func BenchmarkGoogleBreakerAllow(b *testing.B) { 206 breaker := getGoogleBreaker() 207 b.ResetTimer() 208 for i := 0; i <= b.N; i++ { 209 breaker.accept() 210 if i%2 == 0 { 211 breaker.markSuccess() 212 } else { 213 breaker.markFailure() 214 } 215 } 216 } 217 218 func markSuccess(b *googleBreaker, count int) { 219 for i := 0; i < count; i++ { 220 p, err := b.allow() 221 if err != nil { 222 break 223 } 224 p.Accept() 225 } 226 } 227 228 func markFailed(b *googleBreaker, count int) { 229 for i := 0; i < count; i++ { 230 p, err := b.allow() 231 if err == nil { 232 p.Reject() 233 } 234 } 235 }