google.golang.org/grpc@v1.72.2/internal/balancergroup/balancergroup_test.go (about) 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 17 package balancergroup 18 19 import ( 20 "context" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "testing" 25 "time" 26 27 "google.golang.org/grpc/balancer" 28 "google.golang.org/grpc/balancer/pickfirst" 29 "google.golang.org/grpc/balancer/roundrobin" 30 "google.golang.org/grpc/balancer/weightedtarget/weightedaggregator" 31 "google.golang.org/grpc/connectivity" 32 "google.golang.org/grpc/credentials/insecure" 33 "google.golang.org/grpc/internal/balancer/stub" 34 "google.golang.org/grpc/internal/channelz" 35 "google.golang.org/grpc/internal/grpctest" 36 "google.golang.org/grpc/internal/testutils" 37 "google.golang.org/grpc/resolver" 38 ) 39 40 const ( 41 defaultTestTimeout = 5 * time.Second 42 defaultTestShortTimeout = 10 * time.Millisecond 43 ) 44 45 var ( 46 rrBuilder = balancer.Get(roundrobin.Name) 47 testBalancerIDs = []string{"b1", "b2", "b3"} 48 testBackendAddrs []resolver.Address 49 testBackendEndpoints []resolver.Endpoint 50 ) 51 52 const testBackendAddrsCount = 12 53 54 func init() { 55 for i := 0; i < testBackendAddrsCount; i++ { 56 addr := resolver.Address{Addr: fmt.Sprintf("%d.%d.%d.%d:%d", i, i, i, i, i)} 57 testBackendAddrs = append(testBackendAddrs, addr) 58 testBackendEndpoints = append(testBackendEndpoints, resolver.Endpoint{Addresses: []resolver.Address{addr}}) 59 } 60 } 61 62 type s struct { 63 grpctest.Tester 64 } 65 66 func Test(t *testing.T) { 67 grpctest.RunSubTests(t, s{}) 68 } 69 70 // Create a new balancer group, add balancer and backends. 71 // - b1, weight 2, backends [0,1] 72 // - b2, weight 1, backends [2,3] 73 // Start the balancer group and check behavior. 74 // 75 // Close the balancer group. 76 func (s) TestBalancerGroup_start_close(t *testing.T) { 77 cc := testutils.NewBalancerClientConn(t) 78 gator := weightedaggregator.New(cc, nil, testutils.NewTestWRR) 79 gator.Start() 80 bg := New(Options{ 81 CC: cc, 82 BuildOpts: balancer.BuildOptions{}, 83 StateAggregator: gator, 84 Logger: nil, 85 SubBalancerCloseTimeout: time.Duration(0), 86 }) 87 88 // Add two balancers to group and send two resolved addresses to both 89 // balancers. 90 gator.Add(testBalancerIDs[0], 2) 91 bg.Add(testBalancerIDs[0], rrBuilder) 92 bg.UpdateClientConnState(testBalancerIDs[0], balancer.ClientConnState{ResolverState: resolver.State{Endpoints: testBackendEndpoints[0:2]}}) 93 gator.Add(testBalancerIDs[1], 1) 94 bg.Add(testBalancerIDs[1], rrBuilder) 95 bg.UpdateClientConnState(testBalancerIDs[1], balancer.ClientConnState{ResolverState: resolver.State{Endpoints: testBackendEndpoints[2:4]}}) 96 97 m1 := make(map[string]balancer.SubConn) 98 for i := 0; i < 4; i++ { 99 addrs := <-cc.NewSubConnAddrsCh 100 sc := <-cc.NewSubConnCh 101 m1[addrs[0].Addr] = sc 102 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 103 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 104 } 105 106 // Test roundrobin on the last picker. 107 p1 := <-cc.NewPickerCh 108 want := []balancer.SubConn{ 109 m1[testBackendAddrs[0].Addr], m1[testBackendAddrs[0].Addr], 110 m1[testBackendAddrs[1].Addr], m1[testBackendAddrs[1].Addr], 111 m1[testBackendAddrs[2].Addr], m1[testBackendAddrs[3].Addr], 112 } 113 if err := testutils.IsRoundRobin(want, testutils.SubConnFromPicker(p1)); err != nil { 114 t.Fatalf("want %v, got %v", want, err) 115 } 116 117 gator.Stop() 118 bg.Close() 119 for i := 0; i < 4; i++ { 120 (<-cc.ShutdownSubConnCh).UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Shutdown}) 121 } 122 } 123 124 // Test that balancer group start() doesn't deadlock if the balancer calls back 125 // into balancer group inline when it gets an update. 126 // 127 // The potential deadlock can happen if we 128 // - hold a lock and send updates to balancer (e.g. update resolved addresses) 129 // - the balancer calls back (NewSubConn or update picker) in line 130 // 131 // The callback will try to hold the same lock again, which will cause a 132 // deadlock. 133 // 134 // This test starts the balancer group with a test balancer, will update picker 135 // whenever it gets an address update. It's expected that start() doesn't block 136 // because of deadlock. 137 func (s) TestBalancerGroup_start_close_deadlock(t *testing.T) { 138 const balancerName = "stub-TestBalancerGroup_start_close_deadlock" 139 stub.Register(balancerName, stub.BalancerFuncs{}) 140 builder := balancer.Get(balancerName) 141 142 cc := testutils.NewBalancerClientConn(t) 143 gator := weightedaggregator.New(cc, nil, testutils.NewTestWRR) 144 gator.Start() 145 bg := New(Options{ 146 CC: cc, 147 BuildOpts: balancer.BuildOptions{}, 148 StateAggregator: gator, 149 Logger: nil, 150 SubBalancerCloseTimeout: time.Duration(0), 151 }) 152 153 gator.Add(testBalancerIDs[0], 2) 154 bg.Add(testBalancerIDs[0], builder) 155 bg.UpdateClientConnState(testBalancerIDs[0], balancer.ClientConnState{ResolverState: resolver.State{Addresses: testBackendAddrs[0:2]}}) 156 gator.Add(testBalancerIDs[1], 1) 157 bg.Add(testBalancerIDs[1], builder) 158 bg.UpdateClientConnState(testBalancerIDs[1], balancer.ClientConnState{ResolverState: resolver.State{Addresses: testBackendAddrs[2:4]}}) 159 } 160 161 // initBalancerGroupForCachingTest creates a balancer group, and initialize it 162 // to be ready for caching tests. 163 // 164 // Two rr balancers are added to bg, each with 2 ready subConns. A sub-balancer 165 // is removed later, so the balancer group returned has one sub-balancer in its 166 // own map, and one sub-balancer in cache. 167 func initBalancerGroupForCachingTest(t *testing.T, idleCacheTimeout time.Duration) (*weightedaggregator.Aggregator, *BalancerGroup, *testutils.BalancerClientConn, map[string]*testutils.TestSubConn) { 168 cc := testutils.NewBalancerClientConn(t) 169 gator := weightedaggregator.New(cc, nil, testutils.NewTestWRR) 170 gator.Start() 171 bg := New(Options{ 172 CC: cc, 173 BuildOpts: balancer.BuildOptions{}, 174 StateAggregator: gator, 175 Logger: nil, 176 SubBalancerCloseTimeout: idleCacheTimeout, 177 }) 178 179 // Add two balancers to group and send two resolved addresses to both 180 // balancers. 181 gator.Add(testBalancerIDs[0], 2) 182 bg.Add(testBalancerIDs[0], rrBuilder) 183 bg.UpdateClientConnState(testBalancerIDs[0], balancer.ClientConnState{ResolverState: resolver.State{Endpoints: testBackendEndpoints[0:2]}}) 184 gator.Add(testBalancerIDs[1], 1) 185 bg.Add(testBalancerIDs[1], rrBuilder) 186 bg.UpdateClientConnState(testBalancerIDs[1], balancer.ClientConnState{ResolverState: resolver.State{Endpoints: testBackendEndpoints[2:4]}}) 187 188 m1 := make(map[string]*testutils.TestSubConn) 189 for i := 0; i < 4; i++ { 190 addrs := <-cc.NewSubConnAddrsCh 191 sc := <-cc.NewSubConnCh 192 m1[addrs[0].Addr] = sc 193 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 194 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 195 } 196 197 // Test roundrobin on the last picker. 198 p1 := <-cc.NewPickerCh 199 want := []balancer.SubConn{ 200 m1[testBackendAddrs[0].Addr], m1[testBackendAddrs[0].Addr], 201 m1[testBackendAddrs[1].Addr], m1[testBackendAddrs[1].Addr], 202 m1[testBackendAddrs[2].Addr], m1[testBackendAddrs[3].Addr], 203 } 204 if err := testutils.IsRoundRobin(want, testutils.SubConnFromPicker(p1)); err != nil { 205 t.Fatalf("want %v, got %v", want, err) 206 } 207 208 gator.Remove(testBalancerIDs[1]) 209 bg.Remove(testBalancerIDs[1]) 210 // Don't wait for SubConns to be removed after close, because they are only 211 // removed after close timeout. 212 for i := 0; i < 10; i++ { 213 select { 214 case sc := <-cc.ShutdownSubConnCh: 215 t.Fatalf("Got request to shut down subconn %v, want no shut down subconn (because subconns were still in cache)", sc) 216 default: 217 } 218 time.Sleep(time.Millisecond) 219 } 220 // Test roundrobin on the with only sub-balancer0. 221 p2 := <-cc.NewPickerCh 222 want = []balancer.SubConn{ 223 m1[testBackendAddrs[0].Addr], m1[testBackendAddrs[1].Addr], 224 } 225 if err := testutils.IsRoundRobin(want, testutils.SubConnFromPicker(p2)); err != nil { 226 t.Fatalf("want %v, got %v", want, err) 227 } 228 229 return gator, bg, cc, m1 230 } 231 232 // Test that if a sub-balancer is removed, and re-added within close timeout, 233 // the subConns won't be re-created. 234 func (s) TestBalancerGroup_locality_caching(t *testing.T) { 235 gator, bg, cc, addrToSC := initBalancerGroupForCachingTest(t, defaultTestTimeout) 236 237 // Turn down subconn for addr2, shouldn't get picker update because 238 // sub-balancer1 was removed. 239 addrToSC[testBackendAddrs[2].Addr].UpdateState(balancer.SubConnState{ 240 ConnectivityState: connectivity.TransientFailure, 241 ConnectionError: errors.New("test error"), 242 }) 243 for i := 0; i < 10; i++ { 244 select { 245 case <-cc.NewPickerCh: 246 t.Fatalf("Got new picker, want no new picker (because the sub-balancer was removed)") 247 default: 248 } 249 time.Sleep(defaultTestShortTimeout) 250 } 251 252 // Re-add sub-balancer-1, because subconns were in cache, no new subconns 253 // should be created. But a new picker will still be generated, with subconn 254 // states update to date. 255 gator.Add(testBalancerIDs[1], 1) 256 bg.Add(testBalancerIDs[1], rrBuilder) 257 258 p3 := <-cc.NewPickerCh 259 want := []balancer.SubConn{ 260 addrToSC[testBackendAddrs[0].Addr], addrToSC[testBackendAddrs[0].Addr], 261 addrToSC[testBackendAddrs[1].Addr], addrToSC[testBackendAddrs[1].Addr], 262 // addr2 is down, b2 only has addr3 in READY state. 263 addrToSC[testBackendAddrs[3].Addr], addrToSC[testBackendAddrs[3].Addr], 264 } 265 if err := testutils.IsRoundRobin(want, testutils.SubConnFromPicker(p3)); err != nil { 266 t.Fatalf("want %v, got %v", want, err) 267 } 268 269 for i := 0; i < 10; i++ { 270 select { 271 case <-cc.NewSubConnAddrsCh: 272 t.Fatalf("Got new subconn, want no new subconn (because subconns were still in cache)") 273 default: 274 } 275 time.Sleep(defaultTestShortTimeout) 276 } 277 } 278 279 // Sub-balancers are put in cache when they are shut down. If balancer group is 280 // closed within close timeout, all subconns should still be removed 281 // immediately. 282 func (s) TestBalancerGroup_locality_caching_close_group(t *testing.T) { 283 _, bg, cc, addrToSC := initBalancerGroupForCachingTest(t, defaultTestTimeout) 284 285 bg.Close() 286 // The balancer group is closed. The subconns should be shutdown immediately. 287 shutdownTimeout := time.After(time.Millisecond * 500) 288 scToShutdown := map[balancer.SubConn]int{ 289 addrToSC[testBackendAddrs[0].Addr]: 1, 290 addrToSC[testBackendAddrs[1].Addr]: 1, 291 addrToSC[testBackendAddrs[2].Addr]: 1, 292 addrToSC[testBackendAddrs[3].Addr]: 1, 293 } 294 for i := 0; i < len(scToShutdown); i++ { 295 select { 296 case sc := <-cc.ShutdownSubConnCh: 297 c := scToShutdown[sc] 298 if c == 0 { 299 t.Fatalf("Got Shutdown for %v when there's %d shutdown expected", sc, c) 300 } 301 scToShutdown[sc] = c - 1 302 case <-shutdownTimeout: 303 t.Fatalf("timeout waiting for subConns (from balancer in cache) to be shut down") 304 } 305 } 306 } 307 308 // Sub-balancers in cache will be closed if not re-added within timeout, and 309 // subConns will be shut down. 310 func (s) TestBalancerGroup_locality_caching_not_read_within_timeout(t *testing.T) { 311 _, _, cc, addrToSC := initBalancerGroupForCachingTest(t, time.Second) 312 313 // The sub-balancer is not re-added within timeout. The subconns should be 314 // shut down. 315 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 316 defer cancel() 317 scToShutdown := map[balancer.SubConn]int{ 318 addrToSC[testBackendAddrs[2].Addr]: 1, 319 addrToSC[testBackendAddrs[3].Addr]: 1, 320 } 321 for i := 0; i < len(scToShutdown); i++ { 322 select { 323 case sc := <-cc.ShutdownSubConnCh: 324 c := scToShutdown[sc] 325 if c == 0 { 326 t.Fatalf("Got Shutdown for %v when there's %d shutdown expected", sc, c) 327 } 328 scToShutdown[sc] = c - 1 329 case <-ctx.Done(): 330 t.Fatalf("timeout waiting for subConns (from balancer in cache) to be shut down") 331 } 332 } 333 } 334 335 // Wrap the rr builder, so it behaves the same, but has a different name. 336 type noopBalancerBuilderWrapper struct { 337 balancer.Builder 338 } 339 340 func init() { 341 balancer.Register(&noopBalancerBuilderWrapper{Builder: rrBuilder}) 342 } 343 344 func (*noopBalancerBuilderWrapper) Name() string { 345 return "noopBalancerBuilderWrapper" 346 } 347 348 // After removing a sub-balancer, re-add with same ID, but different balancer 349 // builder. Old subconns should be shut down, and new subconns should be created. 350 func (s) TestBalancerGroup_locality_caching_read_with_different_builder(t *testing.T) { 351 gator, bg, cc, addrToSC := initBalancerGroupForCachingTest(t, defaultTestTimeout) 352 353 // Re-add sub-balancer-1, but with a different balancer builder. The 354 // sub-balancer was still in cache, but can't be reused. This should cause 355 // old sub-balancer's subconns to be shut down immediately, and new 356 // subconns to be created. 357 gator.Add(testBalancerIDs[1], 1) 358 bg.Add(testBalancerIDs[1], &noopBalancerBuilderWrapper{rrBuilder}) 359 360 // The cached sub-balancer should be closed, and the subconns should be 361 // shut down immediately. 362 shutdownTimeout := time.After(time.Millisecond * 500) 363 scToShutdown := map[balancer.SubConn]int{ 364 addrToSC[testBackendAddrs[2].Addr]: 1, 365 addrToSC[testBackendAddrs[3].Addr]: 1, 366 } 367 for i := 0; i < len(scToShutdown); i++ { 368 select { 369 case sc := <-cc.ShutdownSubConnCh: 370 c := scToShutdown[sc] 371 if c == 0 { 372 t.Fatalf("Got Shutdown for %v when there's %d shutdown expected", sc, c) 373 } 374 scToShutdown[sc] = c - 1 375 case <-shutdownTimeout: 376 t.Fatalf("timeout waiting for subConns (from balancer in cache) to be shut down") 377 } 378 } 379 380 bg.UpdateClientConnState(testBalancerIDs[1], balancer.ClientConnState{ResolverState: resolver.State{Endpoints: testBackendEndpoints[4:6]}}) 381 382 newSCTimeout := time.After(time.Millisecond * 500) 383 scToAdd := map[string]int{ 384 testBackendAddrs[4].Addr: 1, 385 testBackendAddrs[5].Addr: 1, 386 } 387 for i := 0; i < len(scToAdd); i++ { 388 select { 389 case addr := <-cc.NewSubConnAddrsCh: 390 c := scToAdd[addr[0].Addr] 391 if c == 0 { 392 t.Fatalf("Got newSubConn for %v when there's %d new expected", addr, c) 393 } 394 scToAdd[addr[0].Addr] = c - 1 395 sc := <-cc.NewSubConnCh 396 addrToSC[addr[0].Addr] = sc 397 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 398 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 399 case <-newSCTimeout: 400 t.Fatalf("timeout waiting for subConns (from new sub-balancer) to be newed") 401 } 402 } 403 404 // Test roundrobin on the new picker. 405 p3 := <-cc.NewPickerCh 406 want := []balancer.SubConn{ 407 addrToSC[testBackendAddrs[0].Addr], addrToSC[testBackendAddrs[0].Addr], 408 addrToSC[testBackendAddrs[1].Addr], addrToSC[testBackendAddrs[1].Addr], 409 addrToSC[testBackendAddrs[4].Addr], addrToSC[testBackendAddrs[5].Addr], 410 } 411 if err := testutils.IsRoundRobin(want, testutils.SubConnFromPicker(p3)); err != nil { 412 t.Fatalf("want %v, got %v", want, err) 413 } 414 } 415 416 // After removing a sub-balancer, it will be kept in cache. Make sure that this 417 // sub-balancer's Close is called when the balancer group is closed. 418 func (s) TestBalancerGroup_CloseStopsBalancerInCache(t *testing.T) { 419 const balancerName = "stub-TestBalancerGroup_check_close" 420 closed := make(chan struct{}) 421 stub.Register(balancerName, stub.BalancerFuncs{Close: func(_ *stub.BalancerData) { 422 close(closed) 423 }}) 424 builder := balancer.Get(balancerName) 425 426 gator, bg, _, _ := initBalancerGroupForCachingTest(t, time.Second) 427 428 // Add balancer, and remove 429 gator.Add(testBalancerIDs[2], 1) 430 bg.Add(testBalancerIDs[2], builder) 431 gator.Remove(testBalancerIDs[2]) 432 bg.Remove(testBalancerIDs[2]) 433 434 // Immediately close balancergroup, before the cache timeout. 435 bg.Close() 436 437 // Make sure the removed child balancer is closed eventually. 438 select { 439 case <-closed: 440 case <-time.After(time.Second * 2): 441 t.Fatalf("timeout waiting for the child balancer in cache to be closed") 442 } 443 } 444 445 // TestBalancerGroupBuildOptions verifies that the balancer.BuildOptions passed 446 // to the balancergroup at creation time is passed to child policies. 447 func (s) TestBalancerGroupBuildOptions(t *testing.T) { 448 const ( 449 balancerName = "stubBalancer-TestBalancerGroupBuildOptions" 450 userAgent = "ua" 451 ) 452 453 // Setup the stub balancer such that we can read the build options passed to 454 // it in the UpdateClientConnState method. 455 bOpts := balancer.BuildOptions{ 456 DialCreds: insecure.NewCredentials(), 457 ChannelzParent: channelz.RegisterChannel(nil, "test channel"), 458 CustomUserAgent: userAgent, 459 } 460 stub.Register(balancerName, stub.BalancerFuncs{ 461 UpdateClientConnState: func(bd *stub.BalancerData, _ balancer.ClientConnState) error { 462 if bd.BuildOptions.DialCreds != bOpts.DialCreds || bd.BuildOptions.ChannelzParent != bOpts.ChannelzParent || bd.BuildOptions.CustomUserAgent != bOpts.CustomUserAgent { 463 return fmt.Errorf("buildOptions in child balancer: %v, want %v", bd, bOpts) 464 } 465 return nil 466 }, 467 }) 468 cc := testutils.NewBalancerClientConn(t) 469 bg := New(Options{ 470 CC: cc, 471 BuildOpts: bOpts, 472 StateAggregator: nil, 473 Logger: nil, 474 }) 475 476 // Add the stub balancer build above as a child policy. 477 balancerBuilder := balancer.Get(balancerName) 478 bg.Add(testBalancerIDs[0], balancerBuilder) 479 480 // Send an empty clientConn state change. This should trigger the 481 // verification of the buildOptions being passed to the child policy. 482 if err := bg.UpdateClientConnState(testBalancerIDs[0], balancer.ClientConnState{}); err != nil { 483 t.Fatal(err) 484 } 485 } 486 487 func (s) TestBalancerExitIdleOne(t *testing.T) { 488 const balancerName = "stub-balancer-test-balancergroup-exit-idle-one" 489 exitIdleCh := make(chan struct{}, 1) 490 stub.Register(balancerName, stub.BalancerFuncs{ 491 ExitIdle: func(*stub.BalancerData) { 492 exitIdleCh <- struct{}{} 493 }, 494 }) 495 cc := testutils.NewBalancerClientConn(t) 496 bg := New(Options{ 497 CC: cc, 498 BuildOpts: balancer.BuildOptions{}, 499 StateAggregator: nil, 500 Logger: nil, 501 }) 502 defer bg.Close() 503 504 // Add the stub balancer build above as a child policy. 505 builder := balancer.Get(balancerName) 506 bg.Add(testBalancerIDs[0], builder) 507 508 // Call ExitIdle on the child policy. 509 bg.ExitIdleOne(testBalancerIDs[0]) 510 select { 511 case <-time.After(time.Second): 512 t.Fatal("Timeout when waiting for ExitIdle to be invoked on child policy") 513 case <-exitIdleCh: 514 } 515 } 516 517 // TestBalancerGracefulSwitch tests the graceful switch functionality for a 518 // child of the balancer group. At first, the child is configured as a round 519 // robin load balancer, and thus should behave accordingly. The test then 520 // gracefully switches this child to a custom type which only creates a SubConn 521 // for the second passed in address and also only picks that created SubConn. 522 // The new aggregated picker should reflect this change for the child. 523 func (s) TestBalancerGracefulSwitch(t *testing.T) { 524 cc := testutils.NewBalancerClientConn(t) 525 gator := weightedaggregator.New(cc, nil, testutils.NewTestWRR) 526 gator.Start() 527 bg := New(Options{ 528 CC: cc, 529 BuildOpts: balancer.BuildOptions{}, 530 StateAggregator: gator, 531 Logger: nil, 532 }) 533 gator.Add(testBalancerIDs[0], 1) 534 bg.Add(testBalancerIDs[0], rrBuilder) 535 bg.UpdateClientConnState(testBalancerIDs[0], balancer.ClientConnState{ResolverState: resolver.State{Endpoints: testBackendEndpoints[0:2]}}) 536 537 defer bg.Close() 538 539 m1 := make(map[string]balancer.SubConn) 540 scs := make(map[balancer.SubConn]bool) 541 for i := 0; i < 2; i++ { 542 addrs := <-cc.NewSubConnAddrsCh 543 sc := <-cc.NewSubConnCh 544 m1[addrs[0].Addr] = sc 545 scs[sc] = true 546 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 547 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 548 } 549 550 p1 := <-cc.NewPickerCh 551 want := []balancer.SubConn{ 552 m1[testBackendAddrs[0].Addr], m1[testBackendAddrs[1].Addr], 553 } 554 if err := testutils.IsRoundRobin(want, testutils.SubConnFromPicker(p1)); err != nil { 555 t.Fatal(err) 556 } 557 558 // The balancer type for testBalancersIDs[0] is currently Round Robin. Now, 559 // change it to a balancer that has separate behavior logically (creating 560 // SubConn for second address in address list and always picking that 561 // SubConn), and see if the downstream behavior reflects that change. 562 childPolicyName := t.Name() 563 stub.Register(childPolicyName, stub.BalancerFuncs{ 564 Init: func(bd *stub.BalancerData) { 565 bd.Data = balancer.Get(pickfirst.Name).Build(bd.ClientConn, bd.BuildOptions) 566 }, 567 Close: func(bd *stub.BalancerData) { 568 bd.Data.(balancer.Balancer).Close() 569 }, 570 UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { 571 ccs.ResolverState.Endpoints = ccs.ResolverState.Endpoints[1:] 572 bal := bd.Data.(balancer.Balancer) 573 return bal.UpdateClientConnState(ccs) 574 }, 575 }) 576 cfgJSON := json.RawMessage(fmt.Sprintf(`[{%q: {}}]`, t.Name())) 577 lbCfg, err := ParseConfig(cfgJSON) 578 if err != nil { 579 t.Fatalf("ParseConfig(%s) failed: %v", string(cfgJSON), err) 580 } 581 if err := bg.UpdateClientConnState(testBalancerIDs[0], balancer.ClientConnState{ 582 ResolverState: resolver.State{Endpoints: testBackendEndpoints[2:4]}, 583 BalancerConfig: lbCfg, 584 }); err != nil { 585 t.Fatalf("error updating ClientConn state: %v", err) 586 } 587 588 addrs := <-cc.NewSubConnAddrsCh 589 if addrs[0].Addr != testBackendAddrs[3].Addr { 590 // Verifies forwarded to new created balancer, as the wrapped pick first 591 // balancer will delete first address. 592 t.Fatalf("newSubConn called with wrong address, want: %v, got : %v", testBackendAddrs[3].Addr, addrs[0].Addr) 593 } 594 sc := <-cc.NewSubConnCh 595 596 // Update the pick first balancers SubConn as CONNECTING. This will cause 597 // the pick first balancer to UpdateState() with CONNECTING, which shouldn't send 598 // a Picker update back, as the Graceful Switch process is not complete. 599 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 600 ctx, cancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 601 defer cancel() 602 select { 603 case <-cc.NewPickerCh: 604 t.Fatalf("No new picker should have been sent due to the Graceful Switch process not completing") 605 case <-ctx.Done(): 606 } 607 608 // Update the pick first balancers SubConn as READY. This will cause 609 // the pick first balancer to UpdateState() with READY, which should send a 610 // Picker update back, as the Graceful Switch process is complete. This 611 // Picker should always pick the pick first's created SubConn which 612 // corresponds to address 3. 613 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 614 p2 := <-cc.NewPickerCh 615 pr, err := p2.Pick(balancer.PickInfo{}) 616 if err != nil { 617 t.Fatalf("error picking: %v", err) 618 } 619 if pr.SubConn != sc { 620 t.Fatalf("picker.Pick(), want %v, got %v", sc, pr.SubConn) 621 } 622 623 // The Graceful Switch process completing for the child should cause the 624 // SubConns for the balancer being gracefully switched from to get deleted. 625 ctx, cancel = context.WithTimeout(context.Background(), defaultTestTimeout) 626 defer cancel() 627 for i := 0; i < 2; i++ { 628 select { 629 case <-ctx.Done(): 630 t.Fatalf("error waiting for Shutdown()") 631 case sc := <-cc.ShutdownSubConnCh: 632 // The SubConn shut down should have been one of the two created 633 // SubConns, and both should be deleted. 634 if ok := scs[sc]; ok { 635 delete(scs, sc) 636 continue 637 } else { 638 t.Fatalf("Shutdown called for wrong SubConn %v, want in %v", sc, scs) 639 } 640 } 641 } 642 }