googlebreaker_test.go 5.2 KB

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