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