github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/priority/balancer_test.go (about) 1 /* 2 * 3 * Copyright 2021 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package priority 20 21 import ( 22 "context" 23 "fmt" 24 "testing" 25 "time" 26 27 "github.com/google/go-cmp/cmp" 28 "github.com/hxx258456/ccgo/grpc/balancer" 29 "github.com/hxx258456/ccgo/grpc/balancer/roundrobin" 30 "github.com/hxx258456/ccgo/grpc/connectivity" 31 "github.com/hxx258456/ccgo/grpc/internal/balancer/stub" 32 "github.com/hxx258456/ccgo/grpc/internal/balancergroup" 33 "github.com/hxx258456/ccgo/grpc/internal/grpctest" 34 "github.com/hxx258456/ccgo/grpc/internal/hierarchy" 35 internalserviceconfig "github.com/hxx258456/ccgo/grpc/internal/serviceconfig" 36 "github.com/hxx258456/ccgo/grpc/internal/testutils" 37 "github.com/hxx258456/ccgo/grpc/resolver" 38 ) 39 40 type s struct { 41 grpctest.Tester 42 } 43 44 func Test(t *testing.T) { 45 grpctest.RunSubTests(t, s{}) 46 } 47 48 var testBackendAddrStrs []string 49 50 const ( 51 testBackendAddrsCount = 12 52 testRRBalancerName = "another-round-robin" 53 ) 54 55 type anotherRR struct { 56 balancer.Builder 57 } 58 59 func (*anotherRR) Name() string { 60 return testRRBalancerName 61 } 62 63 func init() { 64 for i := 0; i < testBackendAddrsCount; i++ { 65 testBackendAddrStrs = append(testBackendAddrStrs, fmt.Sprintf("%d.%d.%d.%d:%d", i, i, i, i, i)) 66 } 67 balancergroup.DefaultSubBalancerCloseTimeout = time.Millisecond 68 balancer.Register(&anotherRR{Builder: balancer.Get(roundrobin.Name)}) 69 } 70 71 func subConnFromPicker(t *testing.T, p balancer.Picker) func() balancer.SubConn { 72 return func() balancer.SubConn { 73 scst, err := p.Pick(balancer.PickInfo{}) 74 if err != nil { 75 t.Fatalf("unexpected error from picker.Pick: %v", err) 76 } 77 return scst.SubConn 78 } 79 } 80 81 // When a high priority is ready, adding/removing lower locality doesn't cause 82 // changes. 83 // 84 // Init 0 and 1; 0 is up, use 0; add 2, use 0; remove 2, use 0. 85 func (s) TestPriority_HighPriorityReady(t *testing.T) { 86 cc := testutils.NewTestClientConn(t) 87 bb := balancer.Get(Name) 88 pb := bb.Build(cc, balancer.BuildOptions{}) 89 defer pb.Close() 90 91 // Two children, with priorities [0, 1], each with one backend. 92 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 93 ResolverState: resolver.State{ 94 Addresses: []resolver.Address{ 95 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 96 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 97 }, 98 }, 99 BalancerConfig: &LBConfig{ 100 Children: map[string]*Child{ 101 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 102 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 103 }, 104 Priorities: []string{"child-0", "child-1"}, 105 }, 106 }); err != nil { 107 t.Fatalf("failed to update ClientConn state: %v", err) 108 } 109 110 addrs1 := <-cc.NewSubConnAddrsCh 111 if got, want := addrs1[0].Addr, testBackendAddrStrs[0]; got != want { 112 t.Fatalf("sc is created with addr %v, want %v", got, want) 113 } 114 sc1 := <-cc.NewSubConnCh 115 116 // p0 is ready. 117 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 118 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 119 120 // Test roundrobin with only p0 subconns. 121 p1 := <-cc.NewPickerCh 122 want := []balancer.SubConn{sc1} 123 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p1)); err != nil { 124 t.Fatalf("want %v, got %v", want, err) 125 } 126 127 // Add p2, it shouldn't cause any updates. 128 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 129 ResolverState: resolver.State{ 130 Addresses: []resolver.Address{ 131 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 132 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 133 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[2]}, []string{"child-2"}), 134 }, 135 }, 136 BalancerConfig: &LBConfig{ 137 Children: map[string]*Child{ 138 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 139 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 140 "child-2": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 141 }, 142 Priorities: []string{"child-0", "child-1", "child-2"}, 143 }, 144 }); err != nil { 145 t.Fatalf("failed to update ClientConn state: %v", err) 146 } 147 148 select { 149 case <-cc.NewPickerCh: 150 t.Fatalf("got unexpected new picker") 151 case sc := <-cc.NewSubConnCh: 152 t.Fatalf("got unexpected new SubConn: %s", sc) 153 case sc := <-cc.RemoveSubConnCh: 154 t.Fatalf("got unexpected remove SubConn: %v", sc) 155 case <-time.After(time.Millisecond * 100): 156 } 157 158 // Remove p2, no updates. 159 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 160 ResolverState: resolver.State{ 161 Addresses: []resolver.Address{ 162 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 163 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 164 }, 165 }, 166 BalancerConfig: &LBConfig{ 167 Children: map[string]*Child{ 168 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 169 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 170 }, 171 Priorities: []string{"child-0", "child-1"}, 172 }, 173 }); err != nil { 174 t.Fatalf("failed to update ClientConn state: %v", err) 175 } 176 177 select { 178 case <-cc.NewPickerCh: 179 t.Fatalf("got unexpected new picker") 180 case <-cc.NewSubConnCh: 181 t.Fatalf("got unexpected new SubConn") 182 case <-cc.RemoveSubConnCh: 183 t.Fatalf("got unexpected remove SubConn") 184 case <-time.After(time.Millisecond * 100): 185 } 186 } 187 188 // Lower priority is used when higher priority is not ready. 189 // 190 // Init 0 and 1; 0 is up, use 0; 0 is down, 1 is up, use 1; add 2, use 1; 1 is 191 // down, use 2; remove 2, use 1. 192 func (s) TestPriority_SwitchPriority(t *testing.T) { 193 cc := testutils.NewTestClientConn(t) 194 bb := balancer.Get(Name) 195 pb := bb.Build(cc, balancer.BuildOptions{}) 196 defer pb.Close() 197 198 // Two localities, with priorities [0, 1], each with one backend. 199 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 200 ResolverState: resolver.State{ 201 Addresses: []resolver.Address{ 202 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 203 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 204 }, 205 }, 206 BalancerConfig: &LBConfig{ 207 Children: map[string]*Child{ 208 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 209 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 210 }, 211 Priorities: []string{"child-0", "child-1"}, 212 }, 213 }); err != nil { 214 t.Fatalf("failed to update ClientConn state: %v", err) 215 } 216 217 addrs0 := <-cc.NewSubConnAddrsCh 218 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 219 t.Fatalf("sc is created with addr %v, want %v", got, want) 220 } 221 sc0 := <-cc.NewSubConnCh 222 223 // p0 is ready. 224 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 225 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 226 227 // Test roundrobin with only p0 subconns. 228 p0 := <-cc.NewPickerCh 229 want := []balancer.SubConn{sc0} 230 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p0)); err != nil { 231 t.Fatalf("want %v, got %v", want, err) 232 } 233 234 // Turn down 0, will start and use 1. 235 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 236 237 // Before 1 gets READY, picker should return NoSubConnAvailable, so RPCs 238 // will retry. 239 p1 := <-cc.NewPickerCh 240 for i := 0; i < 5; i++ { 241 if _, err := p1.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 242 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 243 } 244 } 245 246 // Handle SubConn creation from 1. 247 addrs1 := <-cc.NewSubConnAddrsCh 248 if got, want := addrs1[0].Addr, testBackendAddrStrs[1]; got != want { 249 t.Fatalf("sc is created with addr %v, want %v", got, want) 250 } 251 sc1 := <-cc.NewSubConnCh 252 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 253 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 254 255 // Test pick with 1. 256 p2 := <-cc.NewPickerCh 257 for i := 0; i < 5; i++ { 258 gotSCSt, _ := p2.Pick(balancer.PickInfo{}) 259 if !cmp.Equal(gotSCSt.SubConn, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 260 t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc1) 261 } 262 } 263 264 // Add p2, it shouldn't cause any udpates. 265 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 266 ResolverState: resolver.State{ 267 Addresses: []resolver.Address{ 268 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 269 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 270 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[2]}, []string{"child-2"}), 271 }, 272 }, 273 BalancerConfig: &LBConfig{ 274 Children: map[string]*Child{ 275 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 276 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 277 "child-2": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 278 }, 279 Priorities: []string{"child-0", "child-1", "child-2"}, 280 }, 281 }); err != nil { 282 t.Fatalf("failed to update ClientConn state: %v", err) 283 } 284 285 select { 286 case <-cc.NewPickerCh: 287 t.Fatalf("got unexpected new picker") 288 case sc := <-cc.NewSubConnCh: 289 t.Fatalf("got unexpected new SubConn, %s", sc) 290 case <-cc.RemoveSubConnCh: 291 t.Fatalf("got unexpected remove SubConn") 292 case <-time.After(time.Millisecond * 100): 293 } 294 295 // Turn down 1, use 2 296 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 297 298 // Before 2 gets READY, picker should return NoSubConnAvailable, so RPCs 299 // will retry. 300 p3 := <-cc.NewPickerCh 301 for i := 0; i < 5; i++ { 302 if _, err := p3.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 303 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 304 } 305 } 306 307 addrs2 := <-cc.NewSubConnAddrsCh 308 if got, want := addrs2[0].Addr, testBackendAddrStrs[2]; got != want { 309 t.Fatalf("sc is created with addr %v, want %v", got, want) 310 } 311 sc2 := <-cc.NewSubConnCh 312 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 313 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 314 315 // Test pick with 2. 316 p4 := <-cc.NewPickerCh 317 for i := 0; i < 5; i++ { 318 gotSCSt, _ := p4.Pick(balancer.PickInfo{}) 319 if !cmp.Equal(gotSCSt.SubConn, sc2, cmp.AllowUnexported(testutils.TestSubConn{})) { 320 t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc2) 321 } 322 } 323 324 // Remove 2, use 1. 325 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 326 ResolverState: resolver.State{ 327 Addresses: []resolver.Address{ 328 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 329 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 330 }, 331 }, 332 BalancerConfig: &LBConfig{ 333 Children: map[string]*Child{ 334 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 335 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 336 }, 337 Priorities: []string{"child-0", "child-1"}, 338 }, 339 }); err != nil { 340 t.Fatalf("failed to update ClientConn state: %v", err) 341 } 342 343 // p2 SubConns are removed. 344 scToRemove := <-cc.RemoveSubConnCh 345 if !cmp.Equal(scToRemove, sc2, cmp.AllowUnexported(testutils.TestSubConn{})) { 346 t.Fatalf("RemoveSubConn, want %v, got %v", sc2, scToRemove) 347 } 348 349 // Should get an update with 1's old transient failure picker, to override 350 // 2's old picker. 351 p5 := <-cc.NewPickerCh 352 for i := 0; i < 5; i++ { 353 if _, err := p5.Pick(balancer.PickInfo{}); err == nil { 354 t.Fatalf("want pick error non-nil, got nil") 355 } 356 } 357 358 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 359 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 360 361 p6 := <-cc.NewPickerCh 362 for i := 0; i < 5; i++ { 363 gotSCSt, _ := p6.Pick(balancer.PickInfo{}) 364 if !cmp.Equal(gotSCSt.SubConn, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 365 t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc2) 366 } 367 } 368 } 369 370 // Lower priority is used when higher priority turns Connecting from Ready. 371 // Because changing from Ready to Connecting is a failure. 372 // 373 // Init 0 and 1; 0 is up, use 0; 0 is connecting, 1 is up, use 1; 0 is ready, 374 // use 0. 375 func (s) TestPriority_HighPriorityToConnectingFromReady(t *testing.T) { 376 cc := testutils.NewTestClientConn(t) 377 bb := balancer.Get(Name) 378 pb := bb.Build(cc, balancer.BuildOptions{}) 379 defer pb.Close() 380 381 // Two localities, with priorities [0, 1], each with one backend. 382 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 383 ResolverState: resolver.State{ 384 Addresses: []resolver.Address{ 385 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 386 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 387 }, 388 }, 389 BalancerConfig: &LBConfig{ 390 Children: map[string]*Child{ 391 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 392 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 393 }, 394 Priorities: []string{"child-0", "child-1"}, 395 }, 396 }); err != nil { 397 t.Fatalf("failed to update ClientConn state: %v", err) 398 } 399 400 addrs0 := <-cc.NewSubConnAddrsCh 401 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 402 t.Fatalf("sc is created with addr %v, want %v", got, want) 403 } 404 sc0 := <-cc.NewSubConnCh 405 406 // p0 is ready. 407 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 408 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 409 410 // Test roundrobin with only p0 subconns. 411 p0 := <-cc.NewPickerCh 412 want := []balancer.SubConn{sc0} 413 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p0)); err != nil { 414 t.Fatalf("want %v, got %v", want, err) 415 } 416 417 // Turn 0 to Connecting, will start and use 1. Because 0 changing from Ready 418 // to Connecting is a failure. 419 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 420 421 // Before 1 gets READY, picker should return NoSubConnAvailable, so RPCs 422 // will retry. 423 p1 := <-cc.NewPickerCh 424 for i := 0; i < 5; i++ { 425 if _, err := p1.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 426 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 427 } 428 } 429 430 // Handle SubConn creation from 1. 431 addrs1 := <-cc.NewSubConnAddrsCh 432 if got, want := addrs1[0].Addr, testBackendAddrStrs[1]; got != want { 433 t.Fatalf("sc is created with addr %v, want %v", got, want) 434 } 435 sc1 := <-cc.NewSubConnCh 436 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 437 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 438 439 // Test pick with 1. 440 p2 := <-cc.NewPickerCh 441 for i := 0; i < 5; i++ { 442 gotSCSt, _ := p2.Pick(balancer.PickInfo{}) 443 if !cmp.Equal(gotSCSt.SubConn, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 444 t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc1) 445 } 446 } 447 448 // Turn 0 back to Ready. 449 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 450 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 451 452 // p1 subconn should be removed. 453 scToRemove := <-cc.RemoveSubConnCh 454 if !cmp.Equal(scToRemove, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 455 t.Fatalf("RemoveSubConn, want %v, got %v", sc0, scToRemove) 456 } 457 458 p3 := <-cc.NewPickerCh 459 for i := 0; i < 5; i++ { 460 gotSCSt, _ := p3.Pick(balancer.PickInfo{}) 461 if !cmp.Equal(gotSCSt.SubConn, sc0, cmp.AllowUnexported(testutils.TestSubConn{})) { 462 t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc0) 463 } 464 } 465 } 466 467 // Add a lower priority while the higher priority is down. 468 // 469 // Init 0 and 1; 0 and 1 both down; add 2, use 2. 470 func (s) TestPriority_HigherDownWhileAddingLower(t *testing.T) { 471 cc := testutils.NewTestClientConn(t) 472 bb := balancer.Get(Name) 473 pb := bb.Build(cc, balancer.BuildOptions{}) 474 defer pb.Close() 475 476 // Two localities, with different priorities, each with one backend. 477 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 478 ResolverState: resolver.State{ 479 Addresses: []resolver.Address{ 480 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 481 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 482 }, 483 }, 484 BalancerConfig: &LBConfig{ 485 Children: map[string]*Child{ 486 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 487 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 488 }, 489 Priorities: []string{"child-0", "child-1"}, 490 }, 491 }); err != nil { 492 t.Fatalf("failed to update ClientConn state: %v", err) 493 } 494 495 addrs0 := <-cc.NewSubConnAddrsCh 496 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 497 t.Fatalf("sc is created with addr %v, want %v", got, want) 498 } 499 sc0 := <-cc.NewSubConnCh 500 501 // Turn down 0, 1 is used. 502 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 503 504 // Before 1 gets READY, picker should return NoSubConnAvailable, so RPCs 505 // will retry. 506 pFail0 := <-cc.NewPickerCh 507 for i := 0; i < 5; i++ { 508 if _, err := pFail0.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 509 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 510 } 511 } 512 513 addrs1 := <-cc.NewSubConnAddrsCh 514 if got, want := addrs1[0].Addr, testBackendAddrStrs[1]; got != want { 515 t.Fatalf("sc is created with addr %v, want %v", got, want) 516 } 517 sc1 := <-cc.NewSubConnCh 518 // Turn down 1, pick should error. 519 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 520 521 // Test pick failure. 522 pFail1 := <-cc.NewPickerCh 523 for i := 0; i < 5; i++ { 524 if _, err := pFail1.Pick(balancer.PickInfo{}); err == nil { 525 t.Fatalf("want pick error non-nil, got nil") 526 } 527 } 528 529 // Add p2, it should create a new SubConn. 530 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 531 ResolverState: resolver.State{ 532 Addresses: []resolver.Address{ 533 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 534 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 535 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[2]}, []string{"child-2"}), 536 }, 537 }, 538 BalancerConfig: &LBConfig{ 539 Children: map[string]*Child{ 540 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 541 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 542 "child-2": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 543 }, 544 Priorities: []string{"child-0", "child-1", "child-2"}, 545 }, 546 }); err != nil { 547 t.Fatalf("failed to update ClientConn state: %v", err) 548 } 549 550 // A new connecting picker should be updated for the new priority. 551 p0 := <-cc.NewPickerCh 552 for i := 0; i < 5; i++ { 553 if _, err := p0.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 554 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 555 } 556 } 557 558 addrs2 := <-cc.NewSubConnAddrsCh 559 if got, want := addrs2[0].Addr, testBackendAddrStrs[2]; got != want { 560 t.Fatalf("sc is created with addr %v, want %v", got, want) 561 } 562 sc2 := <-cc.NewSubConnCh 563 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 564 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 565 566 // Test pick with 2. 567 p1 := <-cc.NewPickerCh 568 for i := 0; i < 5; i++ { 569 gotSCSt, _ := p1.Pick(balancer.PickInfo{}) 570 if !cmp.Equal(gotSCSt.SubConn, sc2, cmp.AllowUnexported(testutils.TestSubConn{})) { 571 t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc2) 572 } 573 } 574 } 575 576 // When a higher priority becomes available, all lower priorities are closed. 577 // 578 // Init 0,1,2; 0 and 1 down, use 2; 0 up, close 1 and 2. 579 func (s) TestPriority_HigherReadyCloseAllLower(t *testing.T) { 580 // defer time.Sleep(10 * time.Millisecond) 581 582 cc := testutils.NewTestClientConn(t) 583 bb := balancer.Get(Name) 584 pb := bb.Build(cc, balancer.BuildOptions{}) 585 defer pb.Close() 586 587 // Three localities, with priorities [0,1,2], each with one backend. 588 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 589 ResolverState: resolver.State{ 590 Addresses: []resolver.Address{ 591 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 592 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 593 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[2]}, []string{"child-2"}), 594 }, 595 }, 596 BalancerConfig: &LBConfig{ 597 Children: map[string]*Child{ 598 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 599 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 600 "child-2": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 601 }, 602 Priorities: []string{"child-0", "child-1", "child-2"}, 603 }, 604 }); err != nil { 605 t.Fatalf("failed to update ClientConn state: %v", err) 606 } 607 608 addrs0 := <-cc.NewSubConnAddrsCh 609 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 610 t.Fatalf("sc is created with addr %v, want %v", got, want) 611 } 612 sc0 := <-cc.NewSubConnCh 613 614 // Turn down 0, 1 is used. 615 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 616 // Before 1 gets READY, picker should return NoSubConnAvailable, so RPCs 617 // will retry. 618 pFail0 := <-cc.NewPickerCh 619 for i := 0; i < 5; i++ { 620 if _, err := pFail0.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 621 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 622 } 623 } 624 625 addrs1 := <-cc.NewSubConnAddrsCh 626 if got, want := addrs1[0].Addr, testBackendAddrStrs[1]; got != want { 627 t.Fatalf("sc is created with addr %v, want %v", got, want) 628 } 629 sc1 := <-cc.NewSubConnCh 630 631 // Turn down 1, 2 is used. 632 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 633 // Before 2 gets READY, picker should return NoSubConnAvailable, so RPCs 634 // will retry. 635 pFail1 := <-cc.NewPickerCh 636 for i := 0; i < 5; i++ { 637 if _, err := pFail1.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 638 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 639 } 640 } 641 642 addrs2 := <-cc.NewSubConnAddrsCh 643 if got, want := addrs2[0].Addr, testBackendAddrStrs[2]; got != want { 644 t.Fatalf("sc is created with addr %v, want %v", got, want) 645 } 646 sc2 := <-cc.NewSubConnCh 647 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 648 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 649 650 // Test pick with 2. 651 p2 := <-cc.NewPickerCh 652 for i := 0; i < 5; i++ { 653 gotSCSt, _ := p2.Pick(balancer.PickInfo{}) 654 if !cmp.Equal(gotSCSt.SubConn, sc2, cmp.AllowUnexported(testutils.TestSubConn{})) { 655 t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc2) 656 } 657 } 658 659 // When 0 becomes ready, 0 should be used, 1 and 2 should all be closed. 660 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 661 662 // sc1 and sc2 should be removed. 663 // 664 // With localities caching, the lower priorities are closed after a timeout, 665 // in goroutines. The order is no longer guaranteed. 666 scToRemove := []balancer.SubConn{<-cc.RemoveSubConnCh, <-cc.RemoveSubConnCh} 667 if !(cmp.Equal(scToRemove[0], sc1, cmp.AllowUnexported(testutils.TestSubConn{})) && 668 cmp.Equal(scToRemove[1], sc2, cmp.AllowUnexported(testutils.TestSubConn{}))) && 669 !(cmp.Equal(scToRemove[0], sc2, cmp.AllowUnexported(testutils.TestSubConn{})) && 670 cmp.Equal(scToRemove[1], sc1, cmp.AllowUnexported(testutils.TestSubConn{}))) { 671 t.Errorf("RemoveSubConn, want [%v, %v], got %v", sc1, sc2, scToRemove) 672 } 673 674 // Test pick with 0. 675 p0 := <-cc.NewPickerCh 676 for i := 0; i < 5; i++ { 677 gotSCSt, _ := p0.Pick(balancer.PickInfo{}) 678 if !cmp.Equal(gotSCSt.SubConn, sc0, cmp.AllowUnexported(testutils.TestSubConn{})) { 679 t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc0) 680 } 681 } 682 } 683 684 // At init, start the next lower priority after timeout if the higher priority 685 // doesn't get ready. 686 // 687 // Init 0,1; 0 is not ready (in connecting), after timeout, use 1. 688 func (s) TestPriority_InitTimeout(t *testing.T) { 689 const testPriorityInitTimeout = time.Second 690 defer func() func() { 691 old := DefaultPriorityInitTimeout 692 DefaultPriorityInitTimeout = testPriorityInitTimeout 693 return func() { 694 DefaultPriorityInitTimeout = old 695 } 696 }()() 697 698 cc := testutils.NewTestClientConn(t) 699 bb := balancer.Get(Name) 700 pb := bb.Build(cc, balancer.BuildOptions{}) 701 defer pb.Close() 702 703 // Two localities, with different priorities, each with one backend. 704 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 705 ResolverState: resolver.State{ 706 Addresses: []resolver.Address{ 707 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 708 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 709 }, 710 }, 711 BalancerConfig: &LBConfig{ 712 Children: map[string]*Child{ 713 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 714 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 715 }, 716 Priorities: []string{"child-0", "child-1"}, 717 }, 718 }); err != nil { 719 t.Fatalf("failed to update ClientConn state: %v", err) 720 } 721 722 addrs0 := <-cc.NewSubConnAddrsCh 723 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 724 t.Fatalf("sc is created with addr %v, want %v", got, want) 725 } 726 sc0 := <-cc.NewSubConnCh 727 728 // Keep 0 in connecting, 1 will be used after init timeout. 729 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 730 731 // Make sure new SubConn is created before timeout. 732 select { 733 case <-time.After(testPriorityInitTimeout * 3 / 4): 734 case <-cc.NewSubConnAddrsCh: 735 t.Fatalf("Got a new SubConn too early (Within timeout). Expect a new SubConn only after timeout") 736 } 737 738 addrs1 := <-cc.NewSubConnAddrsCh 739 if got, want := addrs1[0].Addr, testBackendAddrStrs[1]; got != want { 740 t.Fatalf("sc is created with addr %v, want %v", got, want) 741 } 742 sc1 := <-cc.NewSubConnCh 743 744 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 745 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 746 747 // Test pick with 1. 748 p1 := <-cc.NewPickerCh 749 for i := 0; i < 5; i++ { 750 gotSCSt, _ := p1.Pick(balancer.PickInfo{}) 751 if !cmp.Equal(gotSCSt.SubConn, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 752 t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc1) 753 } 754 } 755 } 756 757 // EDS removes all priorities, and re-adds them. 758 func (s) TestPriority_RemovesAllPriorities(t *testing.T) { 759 const testPriorityInitTimeout = time.Second 760 defer func() func() { 761 old := DefaultPriorityInitTimeout 762 DefaultPriorityInitTimeout = testPriorityInitTimeout 763 return func() { 764 DefaultPriorityInitTimeout = old 765 } 766 }()() 767 768 cc := testutils.NewTestClientConn(t) 769 bb := balancer.Get(Name) 770 pb := bb.Build(cc, balancer.BuildOptions{}) 771 defer pb.Close() 772 773 // Two localities, with different priorities, each with one backend. 774 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 775 ResolverState: resolver.State{ 776 Addresses: []resolver.Address{ 777 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 778 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 779 }, 780 }, 781 BalancerConfig: &LBConfig{ 782 Children: map[string]*Child{ 783 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 784 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 785 }, 786 Priorities: []string{"child-0", "child-1"}, 787 }, 788 }); err != nil { 789 t.Fatalf("failed to update ClientConn state: %v", err) 790 } 791 792 addrs0 := <-cc.NewSubConnAddrsCh 793 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 794 t.Fatalf("sc is created with addr %v, want %v", got, want) 795 } 796 sc0 := <-cc.NewSubConnCh 797 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 798 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 799 800 // Test roundrobin with only p0 subconns. 801 p0 := <-cc.NewPickerCh 802 want := []balancer.SubConn{sc0} 803 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p0)); err != nil { 804 t.Fatalf("want %v, got %v", want, err) 805 } 806 807 // Remove all priorities. 808 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 809 ResolverState: resolver.State{ 810 Addresses: nil, 811 }, 812 BalancerConfig: &LBConfig{ 813 Children: nil, 814 Priorities: nil, 815 }, 816 }); err != nil { 817 t.Fatalf("failed to update ClientConn state: %v", err) 818 } 819 820 // p0 subconn should be removed. 821 scToRemove := <-cc.RemoveSubConnCh 822 if !cmp.Equal(scToRemove, sc0, cmp.AllowUnexported(testutils.TestSubConn{})) { 823 t.Fatalf("RemoveSubConn, want %v, got %v", sc0, scToRemove) 824 } 825 826 // Test pick return TransientFailure. 827 pFail := <-cc.NewPickerCh 828 for i := 0; i < 5; i++ { 829 if _, err := pFail.Pick(balancer.PickInfo{}); err != ErrAllPrioritiesRemoved { 830 t.Fatalf("want pick error %v, got %v", ErrAllPrioritiesRemoved, err) 831 } 832 } 833 834 // Re-add two localities, with previous priorities, but different backends. 835 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 836 ResolverState: resolver.State{ 837 Addresses: []resolver.Address{ 838 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[2]}, []string{"child-0"}), 839 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[3]}, []string{"child-1"}), 840 }, 841 }, 842 BalancerConfig: &LBConfig{ 843 Children: map[string]*Child{ 844 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 845 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 846 }, 847 Priorities: []string{"child-0", "child-1"}, 848 }, 849 }); err != nil { 850 t.Fatalf("failed to update ClientConn state: %v", err) 851 } 852 853 addrs01 := <-cc.NewSubConnAddrsCh 854 if got, want := addrs01[0].Addr, testBackendAddrStrs[2]; got != want { 855 t.Fatalf("sc is created with addr %v, want %v", got, want) 856 } 857 sc01 := <-cc.NewSubConnCh 858 859 // Don't send any update to p0, so to not override the old state of p0. 860 // Later, connect to p1 and then remove p1. This will fallback to p0, and 861 // will send p0's old picker if they are not correctly removed. 862 863 // p1 will be used after priority init timeout. 864 addrs11 := <-cc.NewSubConnAddrsCh 865 if got, want := addrs11[0].Addr, testBackendAddrStrs[3]; got != want { 866 t.Fatalf("sc is created with addr %v, want %v", got, want) 867 } 868 sc11 := <-cc.NewSubConnCh 869 pb.UpdateSubConnState(sc11, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 870 pb.UpdateSubConnState(sc11, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 871 872 // Test roundrobin with only p1 subconns. 873 p1 := <-cc.NewPickerCh 874 want = []balancer.SubConn{sc11} 875 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p1)); err != nil { 876 t.Fatalf("want %v, got %v", want, err) 877 } 878 879 // Remove p1, to fallback to p0. 880 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 881 ResolverState: resolver.State{ 882 Addresses: []resolver.Address{ 883 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[2]}, []string{"child-0"}), 884 }, 885 }, 886 BalancerConfig: &LBConfig{ 887 Children: map[string]*Child{ 888 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 889 }, 890 Priorities: []string{"child-0"}, 891 }, 892 }); err != nil { 893 t.Fatalf("failed to update ClientConn state: %v", err) 894 } 895 896 // p1 subconn should be removed. 897 scToRemove1 := <-cc.RemoveSubConnCh 898 if !cmp.Equal(scToRemove1, sc11, cmp.AllowUnexported(testutils.TestSubConn{})) { 899 t.Fatalf("RemoveSubConn, want %v, got %v", sc11, scToRemove1) 900 } 901 902 // Test pick return NoSubConn. 903 pFail1 := <-cc.NewPickerCh 904 for i := 0; i < 5; i++ { 905 if scst, err := pFail1.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 906 t.Fatalf("want pick error _, %v, got %v, _ ,%v", balancer.ErrNoSubConnAvailable, scst, err) 907 } 908 } 909 910 // Send an ready update for the p0 sc that was received when re-adding 911 // priorities. 912 pb.UpdateSubConnState(sc01, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 913 pb.UpdateSubConnState(sc01, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 914 915 // Test roundrobin with only p0 subconns. 916 p2 := <-cc.NewPickerCh 917 want = []balancer.SubConn{sc01} 918 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p2)); err != nil { 919 t.Fatalf("want %v, got %v", want, err) 920 } 921 922 select { 923 case <-cc.NewPickerCh: 924 t.Fatalf("got unexpected new picker") 925 case <-cc.NewSubConnCh: 926 t.Fatalf("got unexpected new SubConn") 927 case <-cc.RemoveSubConnCh: 928 t.Fatalf("got unexpected remove SubConn") 929 case <-time.After(time.Millisecond * 100): 930 } 931 } 932 933 // Test the case where the high priority contains no backends. The low priority 934 // will be used. 935 func (s) TestPriority_HighPriorityNoEndpoints(t *testing.T) { 936 cc := testutils.NewTestClientConn(t) 937 bb := balancer.Get(Name) 938 pb := bb.Build(cc, balancer.BuildOptions{}) 939 defer pb.Close() 940 941 // Two localities, with priorities [0, 1], each with one backend. 942 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 943 ResolverState: resolver.State{ 944 Addresses: []resolver.Address{ 945 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 946 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 947 }, 948 }, 949 BalancerConfig: &LBConfig{ 950 Children: map[string]*Child{ 951 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 952 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 953 }, 954 Priorities: []string{"child-0", "child-1"}, 955 }, 956 }); err != nil { 957 t.Fatalf("failed to update ClientConn state: %v", err) 958 } 959 960 addrs1 := <-cc.NewSubConnAddrsCh 961 if got, want := addrs1[0].Addr, testBackendAddrStrs[0]; got != want { 962 t.Fatalf("sc is created with addr %v, want %v", got, want) 963 } 964 sc1 := <-cc.NewSubConnCh 965 966 // p0 is ready. 967 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 968 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 969 970 // Test roundrobin with only p0 subconns. 971 p1 := <-cc.NewPickerCh 972 want := []balancer.SubConn{sc1} 973 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p1)); err != nil { 974 t.Fatalf("want %v, got %v", want, err) 975 } 976 977 // Remove addresses from priority 0, should use p1. 978 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 979 ResolverState: resolver.State{ 980 Addresses: []resolver.Address{ 981 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 982 }, 983 }, 984 BalancerConfig: &LBConfig{ 985 Children: map[string]*Child{ 986 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 987 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 988 }, 989 Priorities: []string{"child-0", "child-1"}, 990 }, 991 }); err != nil { 992 t.Fatalf("failed to update ClientConn state: %v", err) 993 } 994 995 // p0 will remove the subconn, and ClientConn will send a sc update to 996 // shutdown. 997 scToRemove := <-cc.RemoveSubConnCh 998 pb.UpdateSubConnState(scToRemove, balancer.SubConnState{ConnectivityState: connectivity.Shutdown}) 999 1000 addrs2 := <-cc.NewSubConnAddrsCh 1001 if got, want := addrs2[0].Addr, testBackendAddrStrs[1]; got != want { 1002 t.Fatalf("sc is created with addr %v, want %v", got, want) 1003 } 1004 sc2 := <-cc.NewSubConnCh 1005 1006 // Before 1 gets READY, picker should return NoSubConnAvailable, so RPCs 1007 // will retry. 1008 pFail1 := <-cc.NewPickerCh 1009 for i := 0; i < 5; i++ { 1010 if _, err := pFail1.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 1011 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 1012 } 1013 } 1014 1015 // p1 is ready. 1016 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 1017 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 1018 1019 // Test roundrobin with only p1 subconns. 1020 p2 := <-cc.NewPickerCh 1021 want = []balancer.SubConn{sc2} 1022 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p2)); err != nil { 1023 t.Fatalf("want %v, got %v", want, err) 1024 } 1025 } 1026 1027 // Test the case where the first and only priority is removed. 1028 func (s) TestPriority_FirstPriorityUnavailable(t *testing.T) { 1029 const testPriorityInitTimeout = time.Second 1030 defer func(t time.Duration) { 1031 DefaultPriorityInitTimeout = t 1032 }(DefaultPriorityInitTimeout) 1033 DefaultPriorityInitTimeout = testPriorityInitTimeout 1034 1035 cc := testutils.NewTestClientConn(t) 1036 bb := balancer.Get(Name) 1037 pb := bb.Build(cc, balancer.BuildOptions{}) 1038 defer pb.Close() 1039 1040 // One localities, with priorities [0], each with one backend. 1041 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1042 ResolverState: resolver.State{ 1043 Addresses: []resolver.Address{ 1044 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1045 }, 1046 }, 1047 BalancerConfig: &LBConfig{ 1048 Children: map[string]*Child{ 1049 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1050 }, 1051 Priorities: []string{"child-0"}, 1052 }, 1053 }); err != nil { 1054 t.Fatalf("failed to update ClientConn state: %v", err) 1055 } 1056 1057 // Remove the only localities. 1058 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1059 ResolverState: resolver.State{ 1060 Addresses: nil, 1061 }, 1062 BalancerConfig: &LBConfig{ 1063 Children: nil, 1064 Priorities: nil, 1065 }, 1066 }); err != nil { 1067 t.Fatalf("failed to update ClientConn state: %v", err) 1068 } 1069 1070 // Wait after double the init timer timeout, to ensure it doesn't panic. 1071 time.Sleep(testPriorityInitTimeout * 2) 1072 } 1073 1074 // When a child is moved from low priority to high. 1075 // 1076 // Init a(p0) and b(p1); a(p0) is up, use a; move b to p0, a to p1, use b. 1077 func (s) TestPriority_MoveChildToHigherPriority(t *testing.T) { 1078 cc := testutils.NewTestClientConn(t) 1079 bb := balancer.Get(Name) 1080 pb := bb.Build(cc, balancer.BuildOptions{}) 1081 defer pb.Close() 1082 1083 // Two children, with priorities [0, 1], each with one backend. 1084 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1085 ResolverState: resolver.State{ 1086 Addresses: []resolver.Address{ 1087 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1088 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 1089 }, 1090 }, 1091 BalancerConfig: &LBConfig{ 1092 Children: map[string]*Child{ 1093 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1094 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1095 }, 1096 Priorities: []string{"child-0", "child-1"}, 1097 }, 1098 }); err != nil { 1099 t.Fatalf("failed to update ClientConn state: %v", err) 1100 } 1101 1102 addrs1 := <-cc.NewSubConnAddrsCh 1103 if got, want := addrs1[0].Addr, testBackendAddrStrs[0]; got != want { 1104 t.Fatalf("sc is created with addr %v, want %v", got, want) 1105 } 1106 sc1 := <-cc.NewSubConnCh 1107 1108 // p0 is ready. 1109 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 1110 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 1111 1112 // Test roundrobin with only p0 subconns. 1113 p1 := <-cc.NewPickerCh 1114 want := []balancer.SubConn{sc1} 1115 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p1)); err != nil { 1116 t.Fatalf("want %v, got %v", want, err) 1117 } 1118 1119 // Swap child with p0 and p1, the child at lower priority should now be the 1120 // higher priority, and be used. The old SubConn should be closed. 1121 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1122 ResolverState: resolver.State{ 1123 Addresses: []resolver.Address{ 1124 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1125 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 1126 }, 1127 }, 1128 BalancerConfig: &LBConfig{ 1129 Children: map[string]*Child{ 1130 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1131 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1132 }, 1133 Priorities: []string{"child-1", "child-0"}, 1134 }, 1135 }); err != nil { 1136 t.Fatalf("failed to update ClientConn state: %v", err) 1137 } 1138 1139 // When the new child for p0 is changed from the previous child, the 1140 // balancer should immediately update the picker so the picker from old 1141 // child is not used. In this case, the picker becomes a 1142 // no-subconn-available picker because this child is just started. 1143 pFail := <-cc.NewPickerCh 1144 for i := 0; i < 5; i++ { 1145 if _, err := pFail.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 1146 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 1147 } 1148 } 1149 1150 // Old subconn should be removed. 1151 scToRemove := <-cc.RemoveSubConnCh 1152 if !cmp.Equal(scToRemove, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 1153 t.Fatalf("RemoveSubConn, want %v, got %v", sc1, scToRemove) 1154 } 1155 1156 addrs2 := <-cc.NewSubConnAddrsCh 1157 if got, want := addrs2[0].Addr, testBackendAddrStrs[1]; got != want { 1158 t.Fatalf("sc is created with addr %v, want %v", got, want) 1159 } 1160 sc2 := <-cc.NewSubConnCh 1161 1162 // New p0 child is ready. 1163 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 1164 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 1165 1166 // Test roundrobin with only new subconns. 1167 p2 := <-cc.NewPickerCh 1168 want2 := []balancer.SubConn{sc2} 1169 if err := testutils.IsRoundRobin(want2, subConnFromPicker(t, p2)); err != nil { 1170 t.Fatalf("want %v, got %v", want2, err) 1171 } 1172 } 1173 1174 // When a child is in lower priority, and in use (because higher is down), 1175 // move it from low priority to high. 1176 // 1177 // Init a(p0) and b(p1); a(p0) is down, use b; move b to p0, a to p1, use b. 1178 func (s) TestPriority_MoveReadyChildToHigherPriority(t *testing.T) { 1179 cc := testutils.NewTestClientConn(t) 1180 bb := balancer.Get(Name) 1181 pb := bb.Build(cc, balancer.BuildOptions{}) 1182 defer pb.Close() 1183 1184 // Two children, with priorities [0, 1], each with one backend. 1185 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1186 ResolverState: resolver.State{ 1187 Addresses: []resolver.Address{ 1188 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1189 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 1190 }, 1191 }, 1192 BalancerConfig: &LBConfig{ 1193 Children: map[string]*Child{ 1194 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1195 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1196 }, 1197 Priorities: []string{"child-0", "child-1"}, 1198 }, 1199 }); err != nil { 1200 t.Fatalf("failed to update ClientConn state: %v", err) 1201 } 1202 1203 addrs0 := <-cc.NewSubConnAddrsCh 1204 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 1205 t.Fatalf("sc is created with addr %v, want %v", got, want) 1206 } 1207 sc0 := <-cc.NewSubConnCh 1208 1209 // p0 is down. 1210 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 1211 // Before 1 gets READY, picker should return NoSubConnAvailable, so RPCs 1212 // will retry. 1213 pFail0 := <-cc.NewPickerCh 1214 for i := 0; i < 5; i++ { 1215 if _, err := pFail0.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 1216 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 1217 } 1218 } 1219 1220 addrs1 := <-cc.NewSubConnAddrsCh 1221 if got, want := addrs1[0].Addr, testBackendAddrStrs[1]; got != want { 1222 t.Fatalf("sc is created with addr %v, want %v", got, want) 1223 } 1224 sc1 := <-cc.NewSubConnCh 1225 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 1226 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 1227 1228 // Test roundrobin with only p1 subconns. 1229 p0 := <-cc.NewPickerCh 1230 want := []balancer.SubConn{sc1} 1231 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p0)); err != nil { 1232 t.Fatalf("want %v, got %v", want, err) 1233 } 1234 1235 // Swap child with p0 and p1, the child at lower priority should now be the 1236 // higher priority, and be used. The old SubConn should be closed. 1237 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1238 ResolverState: resolver.State{ 1239 Addresses: []resolver.Address{ 1240 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1241 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 1242 }, 1243 }, 1244 BalancerConfig: &LBConfig{ 1245 Children: map[string]*Child{ 1246 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1247 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1248 }, 1249 Priorities: []string{"child-1", "child-0"}, 1250 }, 1251 }); err != nil { 1252 t.Fatalf("failed to update ClientConn state: %v", err) 1253 } 1254 1255 // Old subconn from child-0 should be removed. 1256 scToRemove := <-cc.RemoveSubConnCh 1257 if !cmp.Equal(scToRemove, sc0, cmp.AllowUnexported(testutils.TestSubConn{})) { 1258 t.Fatalf("RemoveSubConn, want %v, got %v", sc0, scToRemove) 1259 } 1260 1261 // Because this was a ready child moved to a higher priority, no new subconn 1262 // or picker should be updated. 1263 select { 1264 case <-cc.NewPickerCh: 1265 t.Fatalf("got unexpected new picker") 1266 case <-cc.NewSubConnCh: 1267 t.Fatalf("got unexpected new SubConn") 1268 case <-cc.RemoveSubConnCh: 1269 t.Fatalf("got unexpected remove SubConn") 1270 case <-time.After(time.Millisecond * 100): 1271 } 1272 } 1273 1274 // When the lowest child is in use, and is removed, should use the higher 1275 // priority child even though it's not ready. 1276 // 1277 // Init a(p0) and b(p1); a(p0) is down, use b; move b to p0, a to p1, use b. 1278 func (s) TestPriority_RemoveReadyLowestChild(t *testing.T) { 1279 cc := testutils.NewTestClientConn(t) 1280 bb := balancer.Get(Name) 1281 pb := bb.Build(cc, balancer.BuildOptions{}) 1282 defer pb.Close() 1283 1284 // Two children, with priorities [0, 1], each with one backend. 1285 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1286 ResolverState: resolver.State{ 1287 Addresses: []resolver.Address{ 1288 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1289 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 1290 }, 1291 }, 1292 BalancerConfig: &LBConfig{ 1293 Children: map[string]*Child{ 1294 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1295 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1296 }, 1297 Priorities: []string{"child-0", "child-1"}, 1298 }, 1299 }); err != nil { 1300 t.Fatalf("failed to update ClientConn state: %v", err) 1301 } 1302 1303 addrs0 := <-cc.NewSubConnAddrsCh 1304 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 1305 t.Fatalf("sc is created with addr %v, want %v", got, want) 1306 } 1307 sc0 := <-cc.NewSubConnCh 1308 1309 // p0 is down. 1310 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 1311 // Before 1 gets READY, picker should return NoSubConnAvailable, so RPCs 1312 // will retry. 1313 pFail0 := <-cc.NewPickerCh 1314 for i := 0; i < 5; i++ { 1315 if _, err := pFail0.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 1316 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 1317 } 1318 } 1319 1320 addrs1 := <-cc.NewSubConnAddrsCh 1321 if got, want := addrs1[0].Addr, testBackendAddrStrs[1]; got != want { 1322 t.Fatalf("sc is created with addr %v, want %v", got, want) 1323 } 1324 sc1 := <-cc.NewSubConnCh 1325 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 1326 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 1327 1328 // Test roundrobin with only p1 subconns. 1329 p0 := <-cc.NewPickerCh 1330 want := []balancer.SubConn{sc1} 1331 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p0)); err != nil { 1332 t.Fatalf("want %v, got %v", want, err) 1333 } 1334 1335 // Remove child with p1, the child at higher priority should now be used. 1336 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1337 ResolverState: resolver.State{ 1338 Addresses: []resolver.Address{ 1339 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1340 }, 1341 }, 1342 BalancerConfig: &LBConfig{ 1343 Children: map[string]*Child{ 1344 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1345 }, 1346 Priorities: []string{"child-0"}, 1347 }, 1348 }); err != nil { 1349 t.Fatalf("failed to update ClientConn state: %v", err) 1350 } 1351 1352 // Old subconn from child-1 should be removed. 1353 scToRemove := <-cc.RemoveSubConnCh 1354 if !cmp.Equal(scToRemove, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 1355 t.Fatalf("RemoveSubConn, want %v, got %v", sc1, scToRemove) 1356 } 1357 1358 pFail := <-cc.NewPickerCh 1359 for i := 0; i < 5; i++ { 1360 if _, err := pFail.Pick(balancer.PickInfo{}); err == nil { 1361 t.Fatalf("want pick error <non-nil>, got %v", err) 1362 } 1363 } 1364 1365 // Because there was no new child, no new subconn should be created. 1366 select { 1367 case <-cc.NewSubConnCh: 1368 t.Fatalf("got unexpected new SubConn") 1369 case <-time.After(time.Millisecond * 100): 1370 } 1371 } 1372 1373 // When a ready child is removed, it's kept in cache. Re-adding doesn't create subconns. 1374 // 1375 // Init 0; 0 is up, use 0; remove 0, only picker is updated, no subconn is 1376 // removed; re-add 0, picker is updated. 1377 func (s) TestPriority_ReadyChildRemovedButInCache(t *testing.T) { 1378 const testChildCacheTimeout = time.Second 1379 defer func() func() { 1380 old := balancergroup.DefaultSubBalancerCloseTimeout 1381 balancergroup.DefaultSubBalancerCloseTimeout = testChildCacheTimeout 1382 return func() { 1383 balancergroup.DefaultSubBalancerCloseTimeout = old 1384 } 1385 }()() 1386 1387 cc := testutils.NewTestClientConn(t) 1388 bb := balancer.Get(Name) 1389 pb := bb.Build(cc, balancer.BuildOptions{}) 1390 defer pb.Close() 1391 1392 // One children, with priorities [0], with one backend. 1393 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1394 ResolverState: resolver.State{ 1395 Addresses: []resolver.Address{ 1396 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1397 }, 1398 }, 1399 BalancerConfig: &LBConfig{ 1400 Children: map[string]*Child{ 1401 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1402 }, 1403 Priorities: []string{"child-0"}, 1404 }, 1405 }); err != nil { 1406 t.Fatalf("failed to update ClientConn state: %v", err) 1407 } 1408 1409 addrs1 := <-cc.NewSubConnAddrsCh 1410 if got, want := addrs1[0].Addr, testBackendAddrStrs[0]; got != want { 1411 t.Fatalf("sc is created with addr %v, want %v", got, want) 1412 } 1413 sc1 := <-cc.NewSubConnCh 1414 1415 // p0 is ready. 1416 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 1417 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 1418 1419 // Test roundrobin with only p0 subconns. 1420 p1 := <-cc.NewPickerCh 1421 want := []balancer.SubConn{sc1} 1422 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p1)); err != nil { 1423 t.Fatalf("want %v, got %v", want, err) 1424 } 1425 1426 // Remove the child, it shouldn't cause any conn changed, but picker should 1427 // be different. 1428 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1429 ResolverState: resolver.State{}, 1430 BalancerConfig: &LBConfig{}, 1431 }); err != nil { 1432 t.Fatalf("failed to update ClientConn state: %v", err) 1433 } 1434 1435 pFail := <-cc.NewPickerCh 1436 for i := 0; i < 5; i++ { 1437 if _, err := pFail.Pick(balancer.PickInfo{}); err != ErrAllPrioritiesRemoved { 1438 t.Fatalf("want pick error %v, got %v", ErrAllPrioritiesRemoved, err) 1439 } 1440 } 1441 1442 // But no conn changes should happen. Child balancer is in cache. 1443 select { 1444 case sc := <-cc.NewSubConnCh: 1445 t.Fatalf("got unexpected new SubConn: %s", sc) 1446 case sc := <-cc.RemoveSubConnCh: 1447 t.Fatalf("got unexpected remove SubConn: %v", sc) 1448 case <-time.After(time.Millisecond * 100): 1449 } 1450 1451 // Re-add the child, shouldn't create new connections. 1452 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1453 ResolverState: resolver.State{ 1454 Addresses: []resolver.Address{ 1455 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1456 }, 1457 }, 1458 BalancerConfig: &LBConfig{ 1459 Children: map[string]*Child{ 1460 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1461 }, 1462 Priorities: []string{"child-0"}, 1463 }, 1464 }); err != nil { 1465 t.Fatalf("failed to update ClientConn state: %v", err) 1466 } 1467 1468 // Test roundrobin with only p0 subconns. 1469 p2 := <-cc.NewPickerCh 1470 want2 := []balancer.SubConn{sc1} 1471 if err := testutils.IsRoundRobin(want2, subConnFromPicker(t, p2)); err != nil { 1472 t.Fatalf("want %v, got %v", want2, err) 1473 } 1474 1475 // But no conn changes should happen. Child balancer is just taken out from 1476 // the cache. 1477 select { 1478 case sc := <-cc.NewSubConnCh: 1479 t.Fatalf("got unexpected new SubConn: %s", sc) 1480 case sc := <-cc.RemoveSubConnCh: 1481 t.Fatalf("got unexpected remove SubConn: %v", sc) 1482 case <-time.After(time.Millisecond * 100): 1483 } 1484 } 1485 1486 // When the policy of a child is changed. 1487 // 1488 // Init 0; 0 is up, use 0; change 0's policy, 0 is used. 1489 func (s) TestPriority_ChildPolicyChange(t *testing.T) { 1490 cc := testutils.NewTestClientConn(t) 1491 bb := balancer.Get(Name) 1492 pb := bb.Build(cc, balancer.BuildOptions{}) 1493 defer pb.Close() 1494 1495 // One children, with priorities [0], with one backend. 1496 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1497 ResolverState: resolver.State{ 1498 Addresses: []resolver.Address{ 1499 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1500 }, 1501 }, 1502 BalancerConfig: &LBConfig{ 1503 Children: map[string]*Child{ 1504 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: roundrobin.Name}}, 1505 }, 1506 Priorities: []string{"child-0"}, 1507 }, 1508 }); err != nil { 1509 t.Fatalf("failed to update ClientConn state: %v", err) 1510 } 1511 1512 addrs1 := <-cc.NewSubConnAddrsCh 1513 if got, want := addrs1[0].Addr, testBackendAddrStrs[0]; got != want { 1514 t.Fatalf("sc is created with addr %v, want %v", got, want) 1515 } 1516 sc1 := <-cc.NewSubConnCh 1517 1518 // p0 is ready. 1519 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 1520 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 1521 1522 // Test roundrobin with only p0 subconns. 1523 p1 := <-cc.NewPickerCh 1524 want := []balancer.SubConn{sc1} 1525 if err := testutils.IsRoundRobin(want, subConnFromPicker(t, p1)); err != nil { 1526 t.Fatalf("want %v, got %v", want, err) 1527 } 1528 1529 // Change the policy for the child (still roundrobin, but with a different 1530 // name). 1531 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1532 ResolverState: resolver.State{ 1533 Addresses: []resolver.Address{ 1534 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1535 }, 1536 }, 1537 BalancerConfig: &LBConfig{ 1538 Children: map[string]*Child{ 1539 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: testRRBalancerName}}, 1540 }, 1541 Priorities: []string{"child-0"}, 1542 }, 1543 }); err != nil { 1544 t.Fatalf("failed to update ClientConn state: %v", err) 1545 } 1546 1547 // Old subconn should be removed. 1548 scToRemove := <-cc.RemoveSubConnCh 1549 if !cmp.Equal(scToRemove, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 1550 t.Fatalf("RemoveSubConn, want %v, got %v", sc1, scToRemove) 1551 } 1552 1553 // A new subconn should be created. 1554 addrs2 := <-cc.NewSubConnAddrsCh 1555 if got, want := addrs2[0].Addr, testBackendAddrStrs[0]; got != want { 1556 t.Fatalf("sc is created with addr %v, want %v", got, want) 1557 } 1558 sc2 := <-cc.NewSubConnCh 1559 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 1560 pb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 1561 1562 // Test pickfirst with the new subconns. 1563 p2 := <-cc.NewPickerCh 1564 want2 := []balancer.SubConn{sc2} 1565 if err := testutils.IsRoundRobin(want2, subConnFromPicker(t, p2)); err != nil { 1566 t.Fatalf("want %v, got %v", want2, err) 1567 } 1568 } 1569 1570 const inlineUpdateBalancerName = "test-inline-update-balancer" 1571 1572 var errTestInlineStateUpdate = fmt.Errorf("don't like addresses, empty or not") 1573 1574 func init() { 1575 stub.Register(inlineUpdateBalancerName, stub.BalancerFuncs{ 1576 UpdateClientConnState: func(bd *stub.BalancerData, opts balancer.ClientConnState) error { 1577 bd.ClientConn.UpdateState(balancer.State{ 1578 ConnectivityState: connectivity.Ready, 1579 Picker: &testutils.TestConstPicker{Err: errTestInlineStateUpdate}, 1580 }) 1581 return nil 1582 }, 1583 }) 1584 } 1585 1586 // When the child policy update picker inline in a handleClientUpdate call 1587 // (e.g., roundrobin handling empty addresses). There could be deadlock caused 1588 // by acquiring a locked mutex. 1589 func (s) TestPriority_ChildPolicyUpdatePickerInline(t *testing.T) { 1590 cc := testutils.NewTestClientConn(t) 1591 bb := balancer.Get(Name) 1592 pb := bb.Build(cc, balancer.BuildOptions{}) 1593 defer pb.Close() 1594 1595 // One children, with priorities [0], with one backend. 1596 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1597 ResolverState: resolver.State{ 1598 Addresses: []resolver.Address{ 1599 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1600 }, 1601 }, 1602 BalancerConfig: &LBConfig{ 1603 Children: map[string]*Child{ 1604 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: inlineUpdateBalancerName}}, 1605 }, 1606 Priorities: []string{"child-0"}, 1607 }, 1608 }); err != nil { 1609 t.Fatalf("failed to update ClientConn state: %v", err) 1610 } 1611 1612 p0 := <-cc.NewPickerCh 1613 for i := 0; i < 5; i++ { 1614 _, err := p0.Pick(balancer.PickInfo{}) 1615 if err != errTestInlineStateUpdate { 1616 t.Fatalf("picker.Pick, got err %q, want err %q", err, errTestInlineStateUpdate) 1617 } 1618 } 1619 } 1620 1621 // When the child policy's configured to ignore reresolution requests, the 1622 // ResolveNow() calls from this child should be all ignored. 1623 func (s) TestPriority_IgnoreReresolutionRequest(t *testing.T) { 1624 cc := testutils.NewTestClientConn(t) 1625 bb := balancer.Get(Name) 1626 pb := bb.Build(cc, balancer.BuildOptions{}) 1627 defer pb.Close() 1628 1629 // One children, with priorities [0], with one backend, reresolution is 1630 // ignored. 1631 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1632 ResolverState: resolver.State{ 1633 Addresses: []resolver.Address{ 1634 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1635 }, 1636 }, 1637 BalancerConfig: &LBConfig{ 1638 Children: map[string]*Child{ 1639 "child-0": { 1640 Config: &internalserviceconfig.BalancerConfig{Name: resolveNowBalancerName}, 1641 IgnoreReresolutionRequests: true, 1642 }, 1643 }, 1644 Priorities: []string{"child-0"}, 1645 }, 1646 }); err != nil { 1647 t.Fatalf("failed to update ClientConn state: %v", err) 1648 } 1649 1650 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 1651 defer cancel() 1652 // This is the balancer.ClientConn that the inner resolverNowBalancer is 1653 // built with. 1654 balancerCCI, err := resolveNowBalancerCCCh.Receive(ctx) 1655 if err != nil { 1656 t.Fatalf("timeout waiting for ClientConn from balancer builder") 1657 } 1658 balancerCC := balancerCCI.(balancer.ClientConn) 1659 1660 // Since IgnoreReresolutionRequests was set to true, all ResolveNow() calls 1661 // should be ignored. 1662 for i := 0; i < 5; i++ { 1663 balancerCC.ResolveNow(resolver.ResolveNowOptions{}) 1664 } 1665 select { 1666 case <-cc.ResolveNowCh: 1667 t.Fatalf("got unexpected ResolveNow() call") 1668 case <-time.After(time.Millisecond * 100): 1669 } 1670 1671 // Send another update to set IgnoreReresolutionRequests to false. 1672 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1673 ResolverState: resolver.State{ 1674 Addresses: []resolver.Address{ 1675 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1676 }, 1677 }, 1678 BalancerConfig: &LBConfig{ 1679 Children: map[string]*Child{ 1680 "child-0": { 1681 Config: &internalserviceconfig.BalancerConfig{Name: resolveNowBalancerName}, 1682 IgnoreReresolutionRequests: false, 1683 }, 1684 }, 1685 Priorities: []string{"child-0"}, 1686 }, 1687 }); err != nil { 1688 t.Fatalf("failed to update ClientConn state: %v", err) 1689 } 1690 1691 // Call ResolveNow() on the CC, it should be forwarded. 1692 balancerCC.ResolveNow(resolver.ResolveNowOptions{}) 1693 select { 1694 case <-cc.ResolveNowCh: 1695 case <-time.After(time.Second): 1696 t.Fatalf("timeout waiting for ResolveNow()") 1697 } 1698 1699 } 1700 1701 // When the child policy's configured to ignore reresolution requests, the 1702 // ResolveNow() calls from this child should be all ignored, from the other 1703 // children are forwarded. 1704 func (s) TestPriority_IgnoreReresolutionRequestTwoChildren(t *testing.T) { 1705 cc := testutils.NewTestClientConn(t) 1706 bb := balancer.Get(Name) 1707 pb := bb.Build(cc, balancer.BuildOptions{}) 1708 defer pb.Close() 1709 1710 // One children, with priorities [0, 1], each with one backend. 1711 // Reresolution is ignored for p0. 1712 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1713 ResolverState: resolver.State{ 1714 Addresses: []resolver.Address{ 1715 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1716 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 1717 }, 1718 }, 1719 BalancerConfig: &LBConfig{ 1720 Children: map[string]*Child{ 1721 "child-0": { 1722 Config: &internalserviceconfig.BalancerConfig{Name: resolveNowBalancerName}, 1723 IgnoreReresolutionRequests: true, 1724 }, 1725 "child-1": { 1726 Config: &internalserviceconfig.BalancerConfig{Name: resolveNowBalancerName}, 1727 }, 1728 }, 1729 Priorities: []string{"child-0", "child-1"}, 1730 }, 1731 }); err != nil { 1732 t.Fatalf("failed to update ClientConn state: %v", err) 1733 } 1734 1735 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 1736 defer cancel() 1737 // This is the balancer.ClientConn from p0. 1738 balancerCCI0, err := resolveNowBalancerCCCh.Receive(ctx) 1739 if err != nil { 1740 t.Fatalf("timeout waiting for ClientConn from balancer builder 0") 1741 } 1742 balancerCC0 := balancerCCI0.(balancer.ClientConn) 1743 1744 // Set p0 to transient failure, p1 will be started. 1745 addrs0 := <-cc.NewSubConnAddrsCh 1746 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 1747 t.Fatalf("sc is created with addr %v, want %v", got, want) 1748 } 1749 sc0 := <-cc.NewSubConnCh 1750 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 1751 1752 // This is the balancer.ClientConn from p1. 1753 ctx1, cancel1 := context.WithTimeout(context.Background(), time.Second) 1754 defer cancel1() 1755 balancerCCI1, err := resolveNowBalancerCCCh.Receive(ctx1) 1756 if err != nil { 1757 t.Fatalf("timeout waiting for ClientConn from balancer builder 1") 1758 } 1759 balancerCC1 := balancerCCI1.(balancer.ClientConn) 1760 1761 // Since IgnoreReresolutionRequests was set to true for p0, ResolveNow() 1762 // from p0 should all be ignored. 1763 for i := 0; i < 5; i++ { 1764 balancerCC0.ResolveNow(resolver.ResolveNowOptions{}) 1765 } 1766 select { 1767 case <-cc.ResolveNowCh: 1768 t.Fatalf("got unexpected ResolveNow() call") 1769 case <-time.After(time.Millisecond * 100): 1770 } 1771 1772 // But IgnoreReresolutionRequests was false for p1, ResolveNow() from p1 1773 // should be forwarded. 1774 balancerCC1.ResolveNow(resolver.ResolveNowOptions{}) 1775 select { 1776 case <-cc.ResolveNowCh: 1777 case <-time.After(time.Second): 1778 t.Fatalf("timeout waiting for ResolveNow()") 1779 } 1780 } 1781 1782 const initIdleBalancerName = "test-init-Idle-balancer" 1783 1784 var errsTestInitIdle = []error{ 1785 fmt.Errorf("init Idle balancer error 0"), 1786 fmt.Errorf("init Idle balancer error 1"), 1787 } 1788 1789 func init() { 1790 for i := 0; i < 2; i++ { 1791 ii := i 1792 stub.Register(fmt.Sprintf("%s-%d", initIdleBalancerName, ii), stub.BalancerFuncs{ 1793 UpdateClientConnState: func(bd *stub.BalancerData, opts balancer.ClientConnState) error { 1794 bd.ClientConn.NewSubConn(opts.ResolverState.Addresses, balancer.NewSubConnOptions{}) 1795 return nil 1796 }, 1797 UpdateSubConnState: func(bd *stub.BalancerData, sc balancer.SubConn, state balancer.SubConnState) { 1798 err := fmt.Errorf("wrong picker error") 1799 if state.ConnectivityState == connectivity.Idle { 1800 err = errsTestInitIdle[ii] 1801 } 1802 bd.ClientConn.UpdateState(balancer.State{ 1803 ConnectivityState: state.ConnectivityState, 1804 Picker: &testutils.TestConstPicker{Err: err}, 1805 }) 1806 }, 1807 }) 1808 } 1809 } 1810 1811 // If the high priorities send initial pickers with Idle state, their pickers 1812 // should get picks, because policies like ringhash starts in Idle, and doesn't 1813 // connect. 1814 // 1815 // Init 0, 1; 0 is Idle, use 0; 0 is down, start 1; 1 is Idle, use 1. 1816 func (s) TestPriority_HighPriorityInitIdle(t *testing.T) { 1817 cc := testutils.NewTestClientConn(t) 1818 bb := balancer.Get(Name) 1819 pb := bb.Build(cc, balancer.BuildOptions{}) 1820 defer pb.Close() 1821 1822 // Two children, with priorities [0, 1], each with one backend. 1823 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1824 ResolverState: resolver.State{ 1825 Addresses: []resolver.Address{ 1826 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1827 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 1828 }, 1829 }, 1830 BalancerConfig: &LBConfig{ 1831 Children: map[string]*Child{ 1832 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: fmt.Sprintf("%s-%d", initIdleBalancerName, 0)}}, 1833 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: fmt.Sprintf("%s-%d", initIdleBalancerName, 1)}}, 1834 }, 1835 Priorities: []string{"child-0", "child-1"}, 1836 }, 1837 }); err != nil { 1838 t.Fatalf("failed to update ClientConn state: %v", err) 1839 } 1840 1841 addrs0 := <-cc.NewSubConnAddrsCh 1842 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 1843 t.Fatalf("sc is created with addr %v, want %v", got, want) 1844 } 1845 sc0 := <-cc.NewSubConnCh 1846 1847 // Send an Idle state update to trigger an Idle picker update. 1848 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Idle}) 1849 p0 := <-cc.NewPickerCh 1850 if pr, err := p0.Pick(balancer.PickInfo{}); err != errsTestInitIdle[0] { 1851 t.Fatalf("pick returned %v, %v, want _, %v", pr, err, errsTestInitIdle[0]) 1852 } 1853 1854 // Turn p0 down, to start p1. 1855 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 1856 // Before 1 gets READY, picker should return NoSubConnAvailable, so RPCs 1857 // will retry. 1858 p1 := <-cc.NewPickerCh 1859 for i := 0; i < 5; i++ { 1860 if _, err := p1.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable { 1861 t.Fatalf("want pick error %v, got %v", balancer.ErrNoSubConnAvailable, err) 1862 } 1863 } 1864 1865 addrs1 := <-cc.NewSubConnAddrsCh 1866 if got, want := addrs1[0].Addr, testBackendAddrStrs[1]; got != want { 1867 t.Fatalf("sc is created with addr %v, want %v", got, want) 1868 } 1869 sc1 := <-cc.NewSubConnCh 1870 // Idle picker from p1 should also be forwarded. 1871 pb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Idle}) 1872 p2 := <-cc.NewPickerCh 1873 if pr, err := p2.Pick(balancer.PickInfo{}); err != errsTestInitIdle[1] { 1874 t.Fatalf("pick returned %v, %v, want _, %v", pr, err, errsTestInitIdle[1]) 1875 } 1876 } 1877 1878 // If the high priorities send initial pickers with Idle state, their pickers 1879 // should get picks, because policies like ringhash starts in Idle, and doesn't 1880 // connect. In this case, if a lower priority is added, it shouldn't switch to 1881 // the lower priority. 1882 // 1883 // Init 0; 0 is Idle, use 0; add 1, use 0. 1884 func (s) TestPriority_AddLowPriorityWhenHighIsInIdle(t *testing.T) { 1885 cc := testutils.NewTestClientConn(t) 1886 bb := balancer.Get(Name) 1887 pb := bb.Build(cc, balancer.BuildOptions{}) 1888 defer pb.Close() 1889 1890 // One child, with priorities [0], one backend. 1891 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1892 ResolverState: resolver.State{ 1893 Addresses: []resolver.Address{ 1894 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1895 }, 1896 }, 1897 BalancerConfig: &LBConfig{ 1898 Children: map[string]*Child{ 1899 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: fmt.Sprintf("%s-%d", initIdleBalancerName, 0)}}, 1900 }, 1901 Priorities: []string{"child-0"}, 1902 }, 1903 }); err != nil { 1904 t.Fatalf("failed to update ClientConn state: %v", err) 1905 } 1906 1907 addrs0 := <-cc.NewSubConnAddrsCh 1908 if got, want := addrs0[0].Addr, testBackendAddrStrs[0]; got != want { 1909 t.Fatalf("sc is created with addr %v, want %v", got, want) 1910 } 1911 sc0 := <-cc.NewSubConnCh 1912 1913 // Send an Idle state update to trigger an Idle picker update. 1914 pb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Idle}) 1915 p0 := <-cc.NewPickerCh 1916 if pr, err := p0.Pick(balancer.PickInfo{}); err != errsTestInitIdle[0] { 1917 t.Fatalf("pick returned %v, %v, want _, %v", pr, err, errsTestInitIdle[0]) 1918 } 1919 1920 // Add 1, should keep using 0. 1921 if err := pb.UpdateClientConnState(balancer.ClientConnState{ 1922 ResolverState: resolver.State{ 1923 Addresses: []resolver.Address{ 1924 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[0]}, []string{"child-0"}), 1925 hierarchy.Set(resolver.Address{Addr: testBackendAddrStrs[1]}, []string{"child-1"}), 1926 }, 1927 }, 1928 BalancerConfig: &LBConfig{ 1929 Children: map[string]*Child{ 1930 "child-0": {Config: &internalserviceconfig.BalancerConfig{Name: fmt.Sprintf("%s-%d", initIdleBalancerName, 0)}}, 1931 "child-1": {Config: &internalserviceconfig.BalancerConfig{Name: fmt.Sprintf("%s-%d", initIdleBalancerName, 1)}}, 1932 }, 1933 Priorities: []string{"child-0", "child-1"}, 1934 }, 1935 }); err != nil { 1936 t.Fatalf("failed to update ClientConn state: %v", err) 1937 } 1938 1939 // The ClientConn state update triggers a priority switch, from p0 -> p0 1940 // (since p0 is still in use). Along with this the update, p0 also gets a 1941 // ClientConn state update, with the addresses, which didn't change in this 1942 // test (this update to the child is necessary in case the addresses are 1943 // different). 1944 // 1945 // The test child policy, initIdleBalancer, blindly calls NewSubConn with 1946 // all the addresses it receives, so this will trigger a NewSubConn with the 1947 // old p0 addresses. (Note that in a real balancer, like roundrobin, no new 1948 // SubConn will be created because the addresses didn't change). 1949 // 1950 // The check below makes sure that the addresses are still from p0, and not 1951 // from p1. This is good enough for the purpose of this test. 1952 addrsNew := <-cc.NewSubConnAddrsCh 1953 if got, want := addrsNew[0].Addr, testBackendAddrStrs[0]; got != want { 1954 // Fail if p1 is started and creates a SubConn. 1955 t.Fatalf("got unexpected call to NewSubConn with addr: %v, want %v", addrsNew, want) 1956 } 1957 }