googlebreaker_test.go 5.2 KB

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