roundrobinbalancer_test.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. package internal
  2. import (
  3. "errors"
  4. "sort"
  5. "strconv"
  6. "testing"
  7. "zero/core/logx"
  8. "zero/core/mathx"
  9. "github.com/golang/mock/gomock"
  10. "github.com/stretchr/testify/assert"
  11. )
  12. func init() {
  13. logx.Disable()
  14. }
  15. func TestRoundRobin_addConn(t *testing.T) {
  16. b := NewRoundRobinBalancer(func(server string) (interface{}, error) {
  17. return mockConn{
  18. server: server,
  19. }, nil
  20. }, func(server string, conn interface{}) error {
  21. return errors.New("error")
  22. }, false)
  23. assert.Nil(t, b.AddConn(KV{
  24. Key: "thekey1",
  25. Val: "thevalue",
  26. }))
  27. assert.EqualValues(t, []serverConn{
  28. {
  29. key: "thekey1",
  30. conn: mockConn{server: "thevalue"},
  31. },
  32. }, b.conns)
  33. assert.EqualValues(t, map[string][]string{
  34. "thevalue": {"thekey1"},
  35. }, b.servers)
  36. assert.EqualValues(t, map[string]string{
  37. "thekey1": "thevalue",
  38. }, b.mapping)
  39. assert.Nil(t, b.AddConn(KV{
  40. Key: "thekey2",
  41. Val: "thevalue",
  42. }))
  43. assert.EqualValues(t, []serverConn{
  44. {
  45. key: "thekey1",
  46. conn: mockConn{server: "thevalue"},
  47. },
  48. {
  49. key: "thekey2",
  50. conn: mockConn{server: "thevalue"},
  51. },
  52. }, b.conns)
  53. assert.EqualValues(t, map[string][]string{
  54. "thevalue": {"thekey1", "thekey2"},
  55. }, b.servers)
  56. assert.EqualValues(t, map[string]string{
  57. "thekey1": "thevalue",
  58. "thekey2": "thevalue",
  59. }, b.mapping)
  60. assert.False(t, b.IsEmpty())
  61. b.RemoveKey("thekey1")
  62. assert.EqualValues(t, []serverConn{
  63. {
  64. key: "thekey2",
  65. conn: mockConn{server: "thevalue"},
  66. },
  67. }, b.conns)
  68. assert.EqualValues(t, map[string][]string{
  69. "thevalue": {"thekey2"},
  70. }, b.servers)
  71. assert.EqualValues(t, map[string]string{
  72. "thekey2": "thevalue",
  73. }, b.mapping)
  74. assert.False(t, b.IsEmpty())
  75. b.RemoveKey("thekey2")
  76. assert.EqualValues(t, []serverConn{}, b.conns)
  77. assert.EqualValues(t, map[string][]string{}, b.servers)
  78. assert.EqualValues(t, map[string]string{}, b.mapping)
  79. assert.True(t, b.IsEmpty())
  80. }
  81. func TestRoundRobin_addConnExclusive(t *testing.T) {
  82. b := NewRoundRobinBalancer(func(server string) (interface{}, error) {
  83. return mockConn{
  84. server: server,
  85. }, nil
  86. }, func(server string, conn interface{}) error {
  87. return nil
  88. }, true)
  89. assert.Nil(t, b.AddConn(KV{
  90. Key: "thekey1",
  91. Val: "thevalue",
  92. }))
  93. assert.EqualValues(t, []serverConn{
  94. {
  95. key: "thekey1",
  96. conn: mockConn{server: "thevalue"},
  97. },
  98. }, b.conns)
  99. assert.EqualValues(t, map[string][]string{
  100. "thevalue": {"thekey1"},
  101. }, b.servers)
  102. assert.EqualValues(t, map[string]string{
  103. "thekey1": "thevalue",
  104. }, b.mapping)
  105. assert.Nil(t, b.AddConn(KV{
  106. Key: "thekey2",
  107. Val: "thevalue",
  108. }))
  109. assert.EqualValues(t, []serverConn{
  110. {
  111. key: "thekey2",
  112. conn: mockConn{server: "thevalue"},
  113. },
  114. }, b.conns)
  115. assert.EqualValues(t, map[string][]string{
  116. "thevalue": {"thekey2"},
  117. }, b.servers)
  118. assert.EqualValues(t, map[string]string{
  119. "thekey2": "thevalue",
  120. }, b.mapping)
  121. assert.False(t, b.IsEmpty())
  122. b.RemoveKey("thekey1")
  123. b.RemoveKey("thekey2")
  124. assert.EqualValues(t, []serverConn{}, b.conns)
  125. assert.EqualValues(t, map[string][]string{}, b.servers)
  126. assert.EqualValues(t, map[string]string{}, b.mapping)
  127. assert.True(t, b.IsEmpty())
  128. }
  129. func TestRoundRobin_addConnDupExclusive(t *testing.T) {
  130. b := NewRoundRobinBalancer(func(server string) (interface{}, error) {
  131. return mockConn{
  132. server: server,
  133. }, nil
  134. }, func(server string, conn interface{}) error {
  135. return errors.New("error")
  136. }, true)
  137. assert.Nil(t, b.AddConn(KV{
  138. Key: "thekey1",
  139. Val: "thevalue",
  140. }))
  141. assert.EqualValues(t, []serverConn{
  142. {
  143. key: "thekey1",
  144. conn: mockConn{server: "thevalue"},
  145. },
  146. }, b.conns)
  147. assert.EqualValues(t, map[string][]string{
  148. "thevalue": {"thekey1"},
  149. }, b.servers)
  150. assert.EqualValues(t, map[string]string{
  151. "thekey1": "thevalue",
  152. }, b.mapping)
  153. assert.Nil(t, b.AddConn(KV{
  154. Key: "thekey",
  155. Val: "anothervalue",
  156. }))
  157. assert.Nil(t, b.AddConn(KV{
  158. Key: "thekey1",
  159. Val: "thevalue",
  160. }))
  161. assert.EqualValues(t, []serverConn{
  162. {
  163. key: "thekey",
  164. conn: mockConn{server: "anothervalue"},
  165. },
  166. {
  167. key: "thekey1",
  168. conn: mockConn{server: "thevalue"},
  169. },
  170. }, b.conns)
  171. assert.EqualValues(t, map[string][]string{
  172. "anothervalue": {"thekey"},
  173. "thevalue": {"thekey1"},
  174. }, b.servers)
  175. assert.EqualValues(t, map[string]string{
  176. "thekey": "anothervalue",
  177. "thekey1": "thevalue",
  178. }, b.mapping)
  179. assert.False(t, b.IsEmpty())
  180. b.RemoveKey("thekey")
  181. b.RemoveKey("thekey1")
  182. assert.EqualValues(t, []serverConn{}, b.conns)
  183. assert.EqualValues(t, map[string][]string{}, b.servers)
  184. assert.EqualValues(t, map[string]string{}, b.mapping)
  185. assert.True(t, b.IsEmpty())
  186. }
  187. func TestRoundRobin_addConnError(t *testing.T) {
  188. b := NewRoundRobinBalancer(func(server string) (interface{}, error) {
  189. return nil, errors.New("error")
  190. }, func(server string, conn interface{}) error {
  191. return nil
  192. }, true)
  193. assert.NotNil(t, b.AddConn(KV{
  194. Key: "thekey1",
  195. Val: "thevalue",
  196. }))
  197. assert.Nil(t, b.conns)
  198. assert.EqualValues(t, map[string][]string{}, b.servers)
  199. assert.EqualValues(t, map[string]string{}, b.mapping)
  200. }
  201. func TestRoundRobin_initialize(t *testing.T) {
  202. b := NewRoundRobinBalancer(func(server string) (interface{}, error) {
  203. return mockConn{
  204. server: server,
  205. }, nil
  206. }, func(server string, conn interface{}) error {
  207. return nil
  208. }, true)
  209. for i := 0; i < 100; i++ {
  210. assert.Nil(t, b.AddConn(KV{
  211. Key: "thekey/" + strconv.Itoa(i),
  212. Val: "thevalue/" + strconv.Itoa(i),
  213. }))
  214. }
  215. m := make(map[int]int)
  216. const total = 1000
  217. for i := 0; i < total; i++ {
  218. b.initialize()
  219. m[b.index]++
  220. }
  221. mi := make(map[interface{}]int, len(m))
  222. for k, v := range m {
  223. mi[k] = v
  224. }
  225. entropy := mathx.CalcEntropy(mi, total)
  226. assert.True(t, entropy > .95)
  227. }
  228. func TestRoundRobin_next(t *testing.T) {
  229. b := NewRoundRobinBalancer(func(server string) (interface{}, error) {
  230. return mockConn{
  231. server: server,
  232. }, nil
  233. }, func(server string, conn interface{}) error {
  234. return errors.New("error")
  235. }, true)
  236. const size = 100
  237. for i := 0; i < size; i++ {
  238. assert.Nil(t, b.AddConn(KV{
  239. Key: "thekey/" + strconv.Itoa(i),
  240. Val: "thevalue/" + strconv.Itoa(i),
  241. }))
  242. }
  243. m := make(map[interface{}]int)
  244. const total = 10000
  245. for i := 0; i < total; i++ {
  246. val, ok := b.Next()
  247. assert.True(t, ok)
  248. m[val]++
  249. }
  250. entropy := mathx.CalcEntropy(m, total)
  251. assert.Equal(t, size, len(m))
  252. assert.True(t, entropy > .95)
  253. for i := 0; i < size; i++ {
  254. b.RemoveKey("thekey/" + strconv.Itoa(i))
  255. }
  256. _, ok := b.Next()
  257. assert.False(t, ok)
  258. }
  259. func TestRoundRobinBalancer_Listener(t *testing.T) {
  260. ctrl := gomock.NewController(t)
  261. defer ctrl.Finish()
  262. b := NewRoundRobinBalancer(func(server string) (interface{}, error) {
  263. return mockConn{
  264. server: server,
  265. }, nil
  266. }, func(server string, conn interface{}) error {
  267. return nil
  268. }, true)
  269. assert.Nil(t, b.AddConn(KV{
  270. Key: "key1",
  271. Val: "val1",
  272. }))
  273. assert.Nil(t, b.AddConn(KV{
  274. Key: "key2",
  275. Val: "val2",
  276. }))
  277. listener := NewMockListener(ctrl)
  278. listener.EXPECT().OnUpdate(gomock.Any(), gomock.Any(), "key2").Do(func(keys, vals, _ interface{}) {
  279. sort.Strings(vals.([]string))
  280. sort.Strings(keys.([]string))
  281. assert.EqualValues(t, []string{"key1", "key2"}, keys)
  282. assert.EqualValues(t, []string{"val1", "val2"}, vals)
  283. })
  284. b.setListener(listener)
  285. b.notify("key2")
  286. }
  287. func TestRoundRobinBalancer_remove(t *testing.T) {
  288. b := NewRoundRobinBalancer(func(server string) (interface{}, error) {
  289. return mockConn{
  290. server: server,
  291. }, nil
  292. }, func(server string, conn interface{}) error {
  293. return nil
  294. }, true)
  295. assert.Nil(t, b.handlePrevious(nil, "any"))
  296. _, ok := b.doRemoveKv("any")
  297. assert.True(t, ok)
  298. }