You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

299 regels
8.0 KiB

  1. /*
  2. * Copyright 2019 gRPC authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package edsbalancer
  17. import (
  18. "fmt"
  19. "testing"
  20. "google.golang.org/grpc/balancer"
  21. "google.golang.org/grpc/connectivity"
  22. "google.golang.org/grpc/resolver"
  23. )
  24. var (
  25. testSubConns = []*testSubConn{{id: "sc1"}, {id: "sc2"}, {id: "sc3"}, {id: "sc4"}}
  26. )
  27. type testSubConn struct {
  28. id string
  29. }
  30. func (tsc *testSubConn) UpdateAddresses([]resolver.Address) {
  31. panic("not implemented")
  32. }
  33. func (tsc *testSubConn) Connect() {
  34. }
  35. // Implement stringer to get human friendly error message.
  36. func (tsc *testSubConn) String() string {
  37. return tsc.id
  38. }
  39. type testClientConn struct {
  40. t *testing.T // For logging only.
  41. newSubConnAddrsCh chan []resolver.Address // The last 10 []Address to create subconn.
  42. newSubConnCh chan balancer.SubConn // The last 10 subconn created.
  43. removeSubConnCh chan balancer.SubConn // The last 10 subconn removed.
  44. newPickerCh chan balancer.Picker // The last picker updated.
  45. newStateCh chan connectivity.State // The last state.
  46. subConnIdx int
  47. }
  48. func newTestClientConn(t *testing.T) *testClientConn {
  49. return &testClientConn{
  50. t: t,
  51. newSubConnAddrsCh: make(chan []resolver.Address, 10),
  52. newSubConnCh: make(chan balancer.SubConn, 10),
  53. removeSubConnCh: make(chan balancer.SubConn, 10),
  54. newPickerCh: make(chan balancer.Picker, 1),
  55. newStateCh: make(chan connectivity.State, 1),
  56. }
  57. }
  58. func (tcc *testClientConn) NewSubConn(a []resolver.Address, o balancer.NewSubConnOptions) (balancer.SubConn, error) {
  59. sc := testSubConns[tcc.subConnIdx]
  60. tcc.subConnIdx++
  61. tcc.t.Logf("testClientConn: NewSubConn(%v, %+v) => %p", a, o, sc)
  62. select {
  63. case tcc.newSubConnAddrsCh <- a:
  64. default:
  65. }
  66. select {
  67. case tcc.newSubConnCh <- sc:
  68. default:
  69. }
  70. return sc, nil
  71. }
  72. func (tcc *testClientConn) RemoveSubConn(sc balancer.SubConn) {
  73. tcc.t.Logf("testClientCOnn: RemoveSubConn(%p)", sc)
  74. select {
  75. case tcc.removeSubConnCh <- sc:
  76. default:
  77. }
  78. }
  79. func (tcc *testClientConn) UpdateBalancerState(s connectivity.State, p balancer.Picker) {
  80. tcc.t.Logf("testClientConn: UpdateBalancerState(%v, %p)", s, p)
  81. select {
  82. case <-tcc.newStateCh:
  83. default:
  84. }
  85. tcc.newStateCh <- s
  86. select {
  87. case <-tcc.newPickerCh:
  88. default:
  89. }
  90. tcc.newPickerCh <- p
  91. }
  92. func (tcc *testClientConn) ResolveNow(resolver.ResolveNowOption) {
  93. panic("not implemented")
  94. }
  95. func (tcc *testClientConn) Target() string {
  96. panic("not implemented")
  97. }
  98. // isRoundRobin checks whether f's return value is roundrobin of elements from
  99. // want. But it doesn't check for the order. Note that want can contain
  100. // duplicate items, which makes it weight-round-robin.
  101. //
  102. // Step 1. the return values of f should form a permutation of all elements in
  103. // want, but not necessary in the same order. E.g. if want is {a,a,b}, the check
  104. // fails if f returns:
  105. // - {a,a,a}: third a is returned before b
  106. // - {a,b,b}: second b is returned before the second a
  107. //
  108. // If error is found in this step, the returned error contains only the first
  109. // iteration until where it goes wrong.
  110. //
  111. // Step 2. the return values of f should be repetitions of the same permutation.
  112. // E.g. if want is {a,a,b}, the check failes if f returns:
  113. // - {a,b,a,b,a,a}: though it satisfies step 1, the second iteration is not
  114. // repeating the first iteration.
  115. //
  116. // If error is found in this step, the returned error contains the first
  117. // iteration + the second iteration until where it goes wrong.
  118. func isRoundRobin(want []balancer.SubConn, f func() balancer.SubConn) error {
  119. wantSet := make(map[balancer.SubConn]int) // SubConn -> count, for weighted RR.
  120. for _, sc := range want {
  121. wantSet[sc]++
  122. }
  123. // The first iteration: makes sure f's return values form a permutation of
  124. // elements in want.
  125. //
  126. // Also keep the returns values in a slice, so we can compare the order in
  127. // the second iteration.
  128. gotSliceFirstIteration := make([]balancer.SubConn, 0, len(want))
  129. for range want {
  130. got := f()
  131. gotSliceFirstIteration = append(gotSliceFirstIteration, got)
  132. wantSet[got]--
  133. if wantSet[got] < 0 {
  134. return fmt.Errorf("non-roundrobin want: %v, result: %v", want, gotSliceFirstIteration)
  135. }
  136. }
  137. // The second iteration should repeat the first iteration.
  138. var gotSliceSecondIteration []balancer.SubConn
  139. for i := 0; i < 2; i++ {
  140. for _, w := range gotSliceFirstIteration {
  141. g := f()
  142. gotSliceSecondIteration = append(gotSliceSecondIteration, g)
  143. if w != g {
  144. return fmt.Errorf("non-roundrobin, first iter: %v, second iter: %v", gotSliceFirstIteration, gotSliceSecondIteration)
  145. }
  146. }
  147. }
  148. return nil
  149. }
  150. // testClosure is a test util for TestIsRoundRobin.
  151. type testClosure struct {
  152. r []balancer.SubConn
  153. i int
  154. }
  155. func (tc *testClosure) next() balancer.SubConn {
  156. ret := tc.r[tc.i]
  157. tc.i = (tc.i + 1) % len(tc.r)
  158. return ret
  159. }
  160. func TestIsRoundRobin(t *testing.T) {
  161. var (
  162. sc1 = testSubConns[0]
  163. sc2 = testSubConns[1]
  164. sc3 = testSubConns[2]
  165. )
  166. testCases := []struct {
  167. desc string
  168. want []balancer.SubConn
  169. got []balancer.SubConn
  170. pass bool
  171. }{
  172. {
  173. desc: "0 element",
  174. want: []balancer.SubConn{},
  175. got: []balancer.SubConn{},
  176. pass: true,
  177. },
  178. {
  179. desc: "1 element RR",
  180. want: []balancer.SubConn{sc1},
  181. got: []balancer.SubConn{sc1, sc1, sc1, sc1},
  182. pass: true,
  183. },
  184. {
  185. desc: "1 element not RR",
  186. want: []balancer.SubConn{sc1},
  187. got: []balancer.SubConn{sc1, sc2, sc1},
  188. pass: false,
  189. },
  190. {
  191. desc: "2 elements RR",
  192. want: []balancer.SubConn{sc1, sc2},
  193. got: []balancer.SubConn{sc1, sc2, sc1, sc2, sc1, sc2},
  194. pass: true,
  195. },
  196. {
  197. desc: "2 elements RR different order from want",
  198. want: []balancer.SubConn{sc2, sc1},
  199. got: []balancer.SubConn{sc1, sc2, sc1, sc2, sc1, sc2},
  200. pass: true,
  201. },
  202. {
  203. desc: "2 elements RR not RR, mistake in first iter",
  204. want: []balancer.SubConn{sc1, sc2},
  205. got: []balancer.SubConn{sc1, sc1, sc1, sc2, sc1, sc2},
  206. pass: false,
  207. },
  208. {
  209. desc: "2 elements RR not RR, mistake in second iter",
  210. want: []balancer.SubConn{sc1, sc2},
  211. got: []balancer.SubConn{sc1, sc2, sc1, sc1, sc1, sc2},
  212. pass: false,
  213. },
  214. {
  215. desc: "2 elements weighted RR",
  216. want: []balancer.SubConn{sc1, sc1, sc2},
  217. got: []balancer.SubConn{sc1, sc1, sc2, sc1, sc1, sc2},
  218. pass: true,
  219. },
  220. {
  221. desc: "2 elements weighted RR different order",
  222. want: []balancer.SubConn{sc1, sc1, sc2},
  223. got: []balancer.SubConn{sc1, sc2, sc1, sc1, sc2, sc1},
  224. pass: true,
  225. },
  226. {
  227. desc: "3 elements RR",
  228. want: []balancer.SubConn{sc1, sc2, sc3},
  229. got: []balancer.SubConn{sc1, sc2, sc3, sc1, sc2, sc3, sc1, sc2, sc3},
  230. pass: true,
  231. },
  232. {
  233. desc: "3 elements RR different order",
  234. want: []balancer.SubConn{sc1, sc2, sc3},
  235. got: []balancer.SubConn{sc3, sc2, sc1, sc3, sc2, sc1},
  236. pass: true,
  237. },
  238. {
  239. desc: "3 elements weighted RR",
  240. want: []balancer.SubConn{sc1, sc1, sc1, sc2, sc2, sc3},
  241. got: []balancer.SubConn{sc1, sc2, sc3, sc1, sc2, sc1, sc1, sc2, sc3, sc1, sc2, sc1},
  242. pass: true,
  243. },
  244. {
  245. desc: "3 elements weighted RR not RR, mistake in first iter",
  246. want: []balancer.SubConn{sc1, sc1, sc1, sc2, sc2, sc3},
  247. got: []balancer.SubConn{sc1, sc2, sc1, sc1, sc2, sc1, sc1, sc2, sc3, sc1, sc2, sc1},
  248. pass: false,
  249. },
  250. {
  251. desc: "3 elements weighted RR not RR, mistake in second iter",
  252. want: []balancer.SubConn{sc1, sc1, sc1, sc2, sc2, sc3},
  253. got: []balancer.SubConn{sc1, sc2, sc3, sc1, sc2, sc1, sc1, sc1, sc3, sc1, sc2, sc1},
  254. pass: false,
  255. },
  256. }
  257. for _, tC := range testCases {
  258. t.Run(tC.desc, func(t *testing.T) {
  259. err := isRoundRobin(tC.want, (&testClosure{r: tC.got}).next)
  260. if err == nil != tC.pass {
  261. t.Errorf("want pass %v, want %v, got err %v", tC.pass, tC.want, err)
  262. }
  263. })
  264. }
  265. }