google.golang.org/grpc@v1.72.2/balancer/pickfirst/pickfirstleaf/pickfirstleaf_ext_test.go (about) 1 /* 2 * 3 * Copyright 2024 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 package pickfirstleaf_test 19 20 import ( 21 "context" 22 "fmt" 23 "sync" 24 "testing" 25 "time" 26 27 "github.com/google/go-cmp/cmp" 28 29 "google.golang.org/grpc" 30 "google.golang.org/grpc/balancer" 31 pfinternal "google.golang.org/grpc/balancer/pickfirst/internal" 32 "google.golang.org/grpc/balancer/pickfirst/pickfirstleaf" 33 "google.golang.org/grpc/codes" 34 "google.golang.org/grpc/connectivity" 35 "google.golang.org/grpc/credentials/insecure" 36 "google.golang.org/grpc/internal" 37 "google.golang.org/grpc/internal/balancer/stub" 38 "google.golang.org/grpc/internal/grpcsync" 39 "google.golang.org/grpc/internal/grpctest" 40 "google.golang.org/grpc/internal/stubserver" 41 "google.golang.org/grpc/internal/testutils" 42 "google.golang.org/grpc/internal/testutils/pickfirst" 43 "google.golang.org/grpc/internal/testutils/stats" 44 "google.golang.org/grpc/metadata" 45 "google.golang.org/grpc/resolver" 46 "google.golang.org/grpc/resolver/manual" 47 "google.golang.org/grpc/status" 48 49 testgrpc "google.golang.org/grpc/interop/grpc_testing" 50 testpb "google.golang.org/grpc/interop/grpc_testing" 51 ) 52 53 const ( 54 // Default timeout for tests in this package. 55 defaultTestTimeout = 10 * time.Second 56 // Default short timeout, to be used when waiting for events which are not 57 // expected to happen. 58 defaultTestShortTimeout = 100 * time.Millisecond 59 stateStoringBalancerName = "state_storing" 60 ) 61 62 var ( 63 stateStoringServiceConfig = fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, stateStoringBalancerName) 64 ignoreBalAttributesOpt = cmp.Transformer("IgnoreBalancerAttributes", func(a resolver.Address) resolver.Address { 65 a.BalancerAttributes = nil 66 return a 67 }) 68 ) 69 70 type s struct { 71 grpctest.Tester 72 } 73 74 func Test(t *testing.T) { 75 grpctest.RunSubTests(t, s{}) 76 } 77 78 // testServer is a server than can be stopped and resumed without closing 79 // the listener. This guarantees the same port number (and address) is used 80 // after restart. When a server is stopped, it accepts and closes all tcp 81 // connections from clients. 82 type testServer struct { 83 stubserver.StubServer 84 lis *testutils.RestartableListener 85 } 86 87 func (s *testServer) stop() { 88 s.lis.Stop() 89 } 90 91 func (s *testServer) resume() { 92 s.lis.Restart() 93 } 94 95 func newTestServer(t *testing.T) *testServer { 96 l, err := testutils.LocalTCPListener() 97 if err != nil { 98 t.Fatalf("Failed to create listener: %v", err) 99 } 100 rl := testutils.NewRestartableListener(l) 101 ss := stubserver.StubServer{ 102 EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) { return &testpb.Empty{}, nil }, 103 Listener: rl, 104 } 105 return &testServer{ 106 StubServer: ss, 107 lis: rl, 108 } 109 } 110 111 // setupPickFirstLeaf performs steps required for pick_first tests. It starts a 112 // bunch of backends exporting the TestService, and creates a ClientConn to them. 113 func setupPickFirstLeaf(t *testing.T, backendCount int, opts ...grpc.DialOption) (*grpc.ClientConn, *manual.Resolver, *backendManager) { 114 t.Helper() 115 r := manual.NewBuilderWithScheme("whatever") 116 backends := make([]*testServer, backendCount) 117 addrs := make([]resolver.Address, backendCount) 118 119 for i := 0; i < backendCount; i++ { 120 server := newTestServer(t) 121 backend := stubserver.StartTestService(t, &server.StubServer) 122 t.Cleanup(func() { 123 backend.Stop() 124 }) 125 backends[i] = server 126 addrs[i] = resolver.Address{Addr: backend.Address} 127 } 128 129 dopts := []grpc.DialOption{ 130 grpc.WithTransportCredentials(insecure.NewCredentials()), 131 grpc.WithResolvers(r), 132 } 133 dopts = append(dopts, opts...) 134 cc, err := grpc.NewClient(r.Scheme()+":///test.server", dopts...) 135 if err != nil { 136 t.Fatalf("grpc.NewClient() failed: %v", err) 137 } 138 t.Cleanup(func() { cc.Close() }) 139 140 // At this point, the resolver has not returned any addresses to the channel. 141 // This RPC must block until the context expires. 142 sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 143 defer sCancel() 144 client := testgrpc.NewTestServiceClient(cc) 145 if _, err := client.EmptyCall(sCtx, &testpb.Empty{}); status.Code(err) != codes.DeadlineExceeded { 146 t.Fatalf("EmptyCall() = %s, want %s", status.Code(err), codes.DeadlineExceeded) 147 } 148 return cc, r, &backendManager{backends} 149 } 150 151 // TestPickFirstLeaf_SimpleResolverUpdate tests the behaviour of the pick first 152 // policy when given an list of addresses. The following steps are carried 153 // out in order: 154 // 1. A list of addresses are given through the resolver. Only one 155 // of the servers is running. 156 // 2. RPCs are sent to verify they reach the running server. 157 // 158 // The state transitions of the ClientConn and all the SubConns created are 159 // verified. 160 func (s) TestPickFirstLeaf_SimpleResolverUpdate_FirstServerReady(t *testing.T) { 161 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 162 defer cancel() 163 balCh := make(chan *stateStoringBalancer, 1) 164 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 165 166 cc, r, bm := setupPickFirstLeaf(t, 2, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 167 addrs := bm.resolverAddrs() 168 stateSubscriber := &ccStateSubscriber{} 169 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 170 171 r.UpdateState(resolver.State{Addresses: addrs}) 172 var bal *stateStoringBalancer 173 select { 174 case bal = <-balCh: 175 case <-ctx.Done(): 176 t.Fatal("Context expired while waiting for balancer to be built") 177 } 178 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 179 180 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil { 181 t.Fatal(err) 182 } 183 184 wantSCStates := []scState{ 185 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Ready}, 186 } 187 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 188 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 189 } 190 191 wantConnStateTransitions := []connectivity.State{ 192 connectivity.Connecting, 193 connectivity.Ready, 194 } 195 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 196 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 197 } 198 } 199 200 func (s) TestPickFirstLeaf_SimpleResolverUpdate_FirstServerUnReady(t *testing.T) { 201 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 202 defer cancel() 203 balCh := make(chan *stateStoringBalancer, 1) 204 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 205 206 cc, r, bm := setupPickFirstLeaf(t, 2, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 207 addrs := bm.resolverAddrs() 208 stateSubscriber := &ccStateSubscriber{} 209 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 210 bm.stopAllExcept(1) 211 212 r.UpdateState(resolver.State{Addresses: addrs}) 213 var bal *stateStoringBalancer 214 select { 215 case bal = <-balCh: 216 case <-ctx.Done(): 217 t.Fatal("Context expired while waiting for balancer to be built") 218 } 219 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 220 221 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 222 t.Fatal(err) 223 } 224 225 wantSCStates := []scState{ 226 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 227 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 228 } 229 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 230 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 231 } 232 233 wantConnStateTransitions := []connectivity.State{ 234 connectivity.Connecting, 235 connectivity.Ready, 236 } 237 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 238 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 239 } 240 } 241 242 func (s) TestPickFirstLeaf_SimpleResolverUpdate_DuplicateAddrs(t *testing.T) { 243 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 244 defer cancel() 245 balCh := make(chan *stateStoringBalancer, 1) 246 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 247 248 cc, r, bm := setupPickFirstLeaf(t, 2, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 249 addrs := bm.resolverAddrs() 250 stateSubscriber := &ccStateSubscriber{} 251 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 252 bm.stopAllExcept(1) 253 254 // Add a duplicate entry in the addresslist 255 r.UpdateState(resolver.State{ 256 Addresses: append([]resolver.Address{addrs[0]}, addrs...), 257 }) 258 var bal *stateStoringBalancer 259 select { 260 case bal = <-balCh: 261 case <-ctx.Done(): 262 t.Fatal("Context expired while waiting for balancer to be built") 263 } 264 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 265 266 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 267 t.Fatal(err) 268 } 269 270 wantSCStates := []scState{ 271 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 272 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 273 } 274 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 275 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 276 } 277 278 wantConnStateTransitions := []connectivity.State{ 279 connectivity.Connecting, 280 connectivity.Ready, 281 } 282 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 283 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 284 } 285 } 286 287 // TestPickFirstLeaf_ResolverUpdates_DisjointLists tests the behaviour of the pick first 288 // policy when the following steps are carried out in order: 289 // 1. A list of addresses are given through the resolver. Only one 290 // of the servers is running. 291 // 2. RPCs are sent to verify they reach the running server. 292 // 3. A second resolver update is sent. Again, only one of the servers is 293 // running. This may not be the same server as before. 294 // 4. RPCs are sent to verify they reach the running server. 295 // 296 // The state transitions of the ClientConn and all the SubConns created are 297 // verified. 298 func (s) TestPickFirstLeaf_ResolverUpdates_DisjointLists(t *testing.T) { 299 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 300 defer cancel() 301 302 balCh := make(chan *stateStoringBalancer, 1) 303 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 304 cc, r, bm := setupPickFirstLeaf(t, 4, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 305 addrs := bm.resolverAddrs() 306 stateSubscriber := &ccStateSubscriber{} 307 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 308 309 bm.backends[0].stop() 310 r.UpdateState(resolver.State{Addresses: []resolver.Address{addrs[0], addrs[1]}}) 311 var bal *stateStoringBalancer 312 select { 313 case bal = <-balCh: 314 case <-ctx.Done(): 315 t.Fatal("Context expired while waiting for balancer to be built") 316 } 317 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 318 319 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 320 t.Fatal(err) 321 } 322 wantSCStates := []scState{ 323 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 324 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 325 } 326 327 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 328 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 329 } 330 331 bm.backends[2].stop() 332 r.UpdateState(resolver.State{Addresses: []resolver.Address{addrs[2], addrs[3]}}) 333 334 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[3]); err != nil { 335 t.Fatal(err) 336 } 337 wantSCStates = []scState{ 338 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 339 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Shutdown}, 340 {Addrs: []resolver.Address{addrs[2]}, State: connectivity.Shutdown}, 341 {Addrs: []resolver.Address{addrs[3]}, State: connectivity.Ready}, 342 } 343 344 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 345 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 346 } 347 348 wantConnStateTransitions := []connectivity.State{ 349 connectivity.Connecting, 350 connectivity.Ready, 351 connectivity.Connecting, 352 connectivity.Ready, 353 } 354 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 355 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 356 } 357 } 358 359 func (s) TestPickFirstLeaf_ResolverUpdates_ActiveBackendInUpdatedList(t *testing.T) { 360 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 361 defer cancel() 362 363 balCh := make(chan *stateStoringBalancer, 1) 364 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 365 cc, r, bm := setupPickFirstLeaf(t, 3, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 366 addrs := bm.resolverAddrs() 367 stateSubscriber := &ccStateSubscriber{} 368 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 369 370 bm.backends[0].stop() 371 r.UpdateState(resolver.State{Addresses: []resolver.Address{addrs[0], addrs[1]}}) 372 var bal *stateStoringBalancer 373 select { 374 case bal = <-balCh: 375 case <-ctx.Done(): 376 t.Fatal("Context expired while waiting for balancer to be built") 377 } 378 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 379 380 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 381 t.Fatal(err) 382 } 383 wantSCStates := []scState{ 384 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 385 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 386 } 387 388 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 389 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 390 } 391 392 bm.backends[2].stop() 393 r.UpdateState(resolver.State{Addresses: []resolver.Address{addrs[2], addrs[1]}}) 394 395 // Verify that the ClientConn stays in READY. 396 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 397 defer sCancel() 398 testutils.AwaitNoStateChange(sCtx, t, cc, connectivity.Ready) 399 400 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 401 t.Fatal(err) 402 } 403 wantSCStates = []scState{ 404 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 405 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 406 } 407 408 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 409 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 410 } 411 412 wantConnStateTransitions := []connectivity.State{ 413 connectivity.Connecting, 414 connectivity.Ready, 415 } 416 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 417 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 418 } 419 } 420 421 func (s) TestPickFirstLeaf_ResolverUpdates_InActiveBackendInUpdatedList(t *testing.T) { 422 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 423 defer cancel() 424 425 balCh := make(chan *stateStoringBalancer, 1) 426 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 427 cc, r, bm := setupPickFirstLeaf(t, 3, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 428 addrs := bm.resolverAddrs() 429 stateSubscriber := &ccStateSubscriber{} 430 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 431 432 bm.backends[0].stop() 433 r.UpdateState(resolver.State{Addresses: []resolver.Address{addrs[0], addrs[1]}}) 434 var bal *stateStoringBalancer 435 select { 436 case bal = <-balCh: 437 case <-ctx.Done(): 438 t.Fatal("Context expired while waiting for balancer to be built") 439 } 440 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 441 442 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 443 t.Fatal(err) 444 } 445 wantSCStates := []scState{ 446 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 447 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 448 } 449 450 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 451 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 452 } 453 454 bm.backends[2].stop() 455 bm.backends[0].resume() 456 457 r.UpdateState(resolver.State{Addresses: []resolver.Address{addrs[0], addrs[2]}}) 458 459 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil { 460 t.Fatal(err) 461 } 462 wantSCStates = []scState{ 463 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 464 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Shutdown}, 465 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Ready}, 466 } 467 468 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 469 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 470 } 471 472 wantConnStateTransitions := []connectivity.State{ 473 connectivity.Connecting, 474 connectivity.Ready, 475 connectivity.Connecting, 476 connectivity.Ready, 477 } 478 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 479 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 480 } 481 } 482 483 func (s) TestPickFirstLeaf_ResolverUpdates_IdenticalLists(t *testing.T) { 484 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 485 defer cancel() 486 487 balCh := make(chan *stateStoringBalancer, 1) 488 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 489 cc, r, bm := setupPickFirstLeaf(t, 2, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 490 addrs := bm.resolverAddrs() 491 stateSubscriber := &ccStateSubscriber{} 492 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 493 494 bm.backends[0].stop() 495 r.UpdateState(resolver.State{Addresses: []resolver.Address{addrs[0], addrs[1]}}) 496 var bal *stateStoringBalancer 497 select { 498 case bal = <-balCh: 499 case <-ctx.Done(): 500 t.Fatal("Context expired while waiting for balancer to be built") 501 } 502 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 503 504 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 505 t.Fatal(err) 506 } 507 wantSCStates := []scState{ 508 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 509 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 510 } 511 512 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 513 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 514 } 515 516 r.UpdateState(resolver.State{Addresses: []resolver.Address{addrs[0], addrs[1]}}) 517 518 // Verify that the ClientConn stays in READY. 519 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 520 defer sCancel() 521 testutils.AwaitNoStateChange(sCtx, t, cc, connectivity.Ready) 522 523 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 524 t.Fatal(err) 525 } 526 wantSCStates = []scState{ 527 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 528 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 529 } 530 531 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 532 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 533 } 534 535 wantConnStateTransitions := []connectivity.State{ 536 connectivity.Connecting, 537 connectivity.Ready, 538 } 539 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 540 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 541 } 542 } 543 544 // TestPickFirstLeaf_StopConnectedServer tests the behaviour of the pick first 545 // policy when the connected server is shut down. It carries out the following 546 // steps in order: 547 // 1. A list of addresses are given through the resolver. Only one 548 // of the servers is running. 549 // 2. The running server is stopped, causing the ClientConn to enter IDLE. 550 // 3. A (possibly different) server is started. 551 // 4. RPCs are made to kick the ClientConn out of IDLE. The test verifies that 552 // the RPCs reach the running server. 553 // 554 // The test verifies the ClientConn state transitions. 555 func (s) TestPickFirstLeaf_StopConnectedServer_FirstServerRestart(t *testing.T) { 556 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 557 defer cancel() 558 559 balCh := make(chan *stateStoringBalancer, 1) 560 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 561 cc, r, bm := setupPickFirstLeaf(t, 2, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 562 addrs := bm.resolverAddrs() 563 stateSubscriber := &ccStateSubscriber{} 564 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 565 566 // shutdown all active backends except the target. 567 bm.stopAllExcept(0) 568 569 r.UpdateState(resolver.State{Addresses: addrs}) 570 var bal *stateStoringBalancer 571 select { 572 case bal = <-balCh: 573 case <-ctx.Done(): 574 t.Fatal("Context expired while waiting for balancer to be built") 575 } 576 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 577 578 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil { 579 t.Fatal(err) 580 } 581 582 wantSCStates := []scState{ 583 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Ready}, 584 } 585 586 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 587 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 588 } 589 590 // Shut down the connected server. 591 bm.backends[0].stop() 592 testutils.AwaitState(ctx, t, cc, connectivity.Idle) 593 594 // Start the new target server. 595 bm.backends[0].resume() 596 597 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil { 598 t.Fatal(err) 599 } 600 601 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 602 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 603 } 604 605 wantConnStateTransitions := []connectivity.State{ 606 connectivity.Connecting, 607 connectivity.Ready, 608 connectivity.Idle, 609 connectivity.Connecting, 610 connectivity.Ready, 611 } 612 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 613 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 614 } 615 } 616 617 func (s) TestPickFirstLeaf_StopConnectedServer_SecondServerRestart(t *testing.T) { 618 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 619 defer cancel() 620 621 balCh := make(chan *stateStoringBalancer, 1) 622 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 623 cc, r, bm := setupPickFirstLeaf(t, 2, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 624 addrs := bm.resolverAddrs() 625 stateSubscriber := &ccStateSubscriber{} 626 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 627 628 // shutdown all active backends except the target. 629 bm.stopAllExcept(1) 630 631 r.UpdateState(resolver.State{Addresses: addrs}) 632 var bal *stateStoringBalancer 633 select { 634 case bal = <-balCh: 635 case <-ctx.Done(): 636 t.Fatal("Context expired while waiting for balancer to be built") 637 } 638 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 639 640 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 641 t.Fatal(err) 642 } 643 644 wantSCStates := []scState{ 645 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 646 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 647 } 648 649 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 650 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 651 } 652 653 // Shut down the connected server. 654 bm.backends[1].stop() 655 testutils.AwaitState(ctx, t, cc, connectivity.Idle) 656 657 // Start the new target server. 658 bm.backends[1].resume() 659 660 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 661 t.Fatal(err) 662 } 663 664 wantSCStates = []scState{ 665 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 666 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 667 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 668 } 669 670 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 671 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 672 } 673 674 wantConnStateTransitions := []connectivity.State{ 675 connectivity.Connecting, 676 connectivity.Ready, 677 connectivity.Idle, 678 connectivity.Connecting, 679 connectivity.Ready, 680 } 681 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 682 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 683 } 684 } 685 686 func (s) TestPickFirstLeaf_StopConnectedServer_SecondServerToFirst(t *testing.T) { 687 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 688 defer cancel() 689 690 balCh := make(chan *stateStoringBalancer, 1) 691 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 692 cc, r, bm := setupPickFirstLeaf(t, 2, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 693 addrs := bm.resolverAddrs() 694 stateSubscriber := &ccStateSubscriber{} 695 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 696 697 // shutdown all active backends except the target. 698 bm.stopAllExcept(1) 699 700 r.UpdateState(resolver.State{Addresses: addrs}) 701 var bal *stateStoringBalancer 702 select { 703 case bal = <-balCh: 704 case <-ctx.Done(): 705 t.Fatal("Context expired while waiting for balancer to be built") 706 } 707 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 708 709 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 710 t.Fatal(err) 711 } 712 713 wantSCStates := []scState{ 714 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 715 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 716 } 717 718 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 719 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 720 } 721 722 // Shut down the connected server. 723 bm.backends[1].stop() 724 testutils.AwaitState(ctx, t, cc, connectivity.Idle) 725 726 // Start the new target server. 727 bm.backends[0].resume() 728 729 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil { 730 t.Fatal(err) 731 } 732 733 wantSCStates = []scState{ 734 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 735 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Shutdown}, 736 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Ready}, 737 } 738 739 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 740 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 741 } 742 743 wantConnStateTransitions := []connectivity.State{ 744 connectivity.Connecting, 745 connectivity.Ready, 746 connectivity.Idle, 747 connectivity.Connecting, 748 connectivity.Ready, 749 } 750 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 751 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 752 } 753 } 754 755 func (s) TestPickFirstLeaf_StopConnectedServer_FirstServerToSecond(t *testing.T) { 756 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 757 defer cancel() 758 759 balCh := make(chan *stateStoringBalancer, 1) 760 balancer.Register(&stateStoringBalancerBuilder{balancer: balCh}) 761 cc, r, bm := setupPickFirstLeaf(t, 2, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 762 addrs := bm.resolverAddrs() 763 stateSubscriber := &ccStateSubscriber{} 764 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 765 766 // shutdown all active backends except the target. 767 bm.stopAllExcept(0) 768 769 r.UpdateState(resolver.State{Addresses: addrs}) 770 var bal *stateStoringBalancer 771 select { 772 case bal = <-balCh: 773 case <-ctx.Done(): 774 t.Fatal("Context expired while waiting for balancer to be built") 775 } 776 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 777 778 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil { 779 t.Fatal(err) 780 } 781 782 wantSCStates := []scState{ 783 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Ready}, 784 } 785 786 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 787 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 788 } 789 790 // Shut down the connected server. 791 bm.backends[0].stop() 792 testutils.AwaitState(ctx, t, cc, connectivity.Idle) 793 794 // Start the new target server. 795 bm.backends[1].resume() 796 797 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 798 t.Fatal(err) 799 } 800 801 wantSCStates = []scState{ 802 {Addrs: []resolver.Address{addrs[0]}, State: connectivity.Shutdown}, 803 {Addrs: []resolver.Address{addrs[1]}, State: connectivity.Ready}, 804 } 805 806 if diff := cmp.Diff(wantSCStates, bal.subConnStates(), ignoreBalAttributesOpt); diff != "" { 807 t.Errorf("SubConn states mismatch (-want +got):\n%s", diff) 808 } 809 810 wantConnStateTransitions := []connectivity.State{ 811 connectivity.Connecting, 812 connectivity.Ready, 813 connectivity.Idle, 814 connectivity.Connecting, 815 connectivity.Ready, 816 } 817 if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" { 818 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 819 } 820 } 821 822 // TestPickFirstLeaf_EmptyAddressList carries out the following steps in order: 823 // 1. Send a resolver update with one running backend. 824 // 2. Send an empty address list causing the balancer to enter TRANSIENT_FAILURE. 825 // 3. Send a resolver update with one running backend. 826 // The test verifies the ClientConn state transitions. 827 func (s) TestPickFirstLeaf_EmptyAddressList(t *testing.T) { 828 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 829 defer cancel() 830 balChan := make(chan *stateStoringBalancer, 1) 831 balancer.Register(&stateStoringBalancerBuilder{balancer: balChan}) 832 cc, r, bm := setupPickFirstLeaf(t, 1, grpc.WithDefaultServiceConfig(stateStoringServiceConfig)) 833 addrs := bm.resolverAddrs() 834 835 stateSubscriber := &ccStateSubscriber{} 836 internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(cc, stateSubscriber) 837 838 r.UpdateState(resolver.State{Addresses: addrs}) 839 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 840 841 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil { 842 t.Fatal(err) 843 } 844 845 r.UpdateState(resolver.State{}) 846 testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure) 847 848 // The balancer should have entered transient failure. 849 // It should transition to CONNECTING from TRANSIENT_FAILURE as sticky TF 850 // only applies when the initial TF is reported due to connection failures 851 // and not bad resolver states. 852 r.UpdateState(resolver.State{Addresses: addrs}) 853 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 854 855 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil { 856 t.Fatal(err) 857 } 858 859 wantTransitions := []connectivity.State{ 860 // From first resolver update. 861 connectivity.Connecting, 862 connectivity.Ready, 863 // From second update. 864 connectivity.TransientFailure, 865 // From third update. 866 connectivity.Connecting, 867 connectivity.Ready, 868 } 869 870 if diff := cmp.Diff(wantTransitions, stateSubscriber.transitions()); diff != "" { 871 t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) 872 } 873 } 874 875 // Test verifies that pickfirst correctly detects the end of the first happy 876 // eyeballs pass when the timer causes pickfirst to reach the end of the address 877 // list and failures are reported out of order. 878 func (s) TestPickFirstLeaf_HappyEyeballs_TF_AfterEndOfList(t *testing.T) { 879 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 880 defer cancel() 881 882 originalTimer := pfinternal.TimeAfterFunc 883 defer func() { 884 pfinternal.TimeAfterFunc = originalTimer 885 }() 886 triggerTimer, timeAfter := mockTimer() 887 pfinternal.TimeAfterFunc = timeAfter 888 889 tmr := stats.NewTestMetricsRecorder() 890 dialer := testutils.NewBlockingDialer() 891 opts := []grpc.DialOption{ 892 grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, pickfirstleaf.Name)), 893 grpc.WithContextDialer(dialer.DialContext), 894 grpc.WithStatsHandler(tmr), 895 } 896 cc, rb, bm := setupPickFirstLeaf(t, 3, opts...) 897 addrs := bm.resolverAddrs() 898 holds := bm.holds(dialer) 899 rb.UpdateState(resolver.State{Addresses: addrs}) 900 cc.Connect() 901 902 testutils.AwaitState(ctx, t, cc, connectivity.Connecting) 903 904 // Verify that only the first server is contacted. 905 if holds[0].Wait(ctx) != true { 906 t.Fatalf("Timeout waiting for server %d with address %q to be contacted", 0, addrs[0]) 907 } 908 if holds[1].IsStarted() != false { 909 t.Fatalf("Server %d with address %q contacted unexpectedly", 1, addrs[1]) 910 } 911 if holds[2].IsStarted() != false { 912 t.Fatalf("Server %d with address %q contacted unexpectedly", 2, addrs[2]) 913 } 914 915 // Make the happy eyeballs timer fire once and verify that the 916 // second server is contacted, but the third isn't. 917 triggerTimer() 918 if holds[1].Wait(ctx) != true { 919 t.Fatalf("Timeout waiting for server %d with address %q to be contacted", 1, addrs[1]) 920 } 921 if holds[2].IsStarted() != false { 922 t.Fatalf("Server %d with address %q contacted unexpectedly", 2, addrs[2]) 923 } 924 925 // Make the happy eyeballs timer fire once more and verify that the 926 // third server is contacted. 927 triggerTimer() 928 if holds[2].Wait(ctx) != true { 929 t.Fatalf("Timeout waiting for server %d with address %q to be contacted", 2, addrs[2]) 930 } 931 932 // First SubConn Fails. 933 holds[0].Fail(fmt.Errorf("test error")) 934 tmr.WaitForInt64CountIncr(ctx, 1) 935 936 // No TF should be reported until the first pass is complete. 937 shortCtx, shortCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 938 defer shortCancel() 939 testutils.AwaitNotState(shortCtx, t, cc, connectivity.TransientFailure) 940 941 // Third SubConn fails. 942 shortCtx, shortCancel = context.WithTimeout(ctx, defaultTestShortTimeout) 943 defer shortCancel() 944 holds[2].Fail(fmt.Errorf("test error")) 945 tmr.WaitForInt64CountIncr(ctx, 1) 946 testutils.AwaitNotState(shortCtx, t, cc, connectivity.TransientFailure) 947 948 // Last SubConn fails, this should result in a TF update. 949 holds[1].Fail(fmt.Errorf("test error")) 950 tmr.WaitForInt64CountIncr(ctx, 1) 951 testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure) 952 953 // Only connection attempt fails in this test. 954 if got, _ := tmr.Metric("grpc.lb.pick_first.connection_attempts_succeeded"); got != 0 { 955 t.Errorf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.pick_first.connection_attempts_succeeded", got, 0) 956 } 957 if got, _ := tmr.Metric("grpc.lb.pick_first.connection_attempts_failed"); got != 1 { 958 t.Errorf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.pick_first.connection_attempts_failed", got, 1) 959 } 960 if got, _ := tmr.Metric("grpc.lb.pick_first.disconnections"); got != 0 { 961 t.Errorf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.pick_first.disconnections", got, 0) 962 } 963 } 964 965 // Test verifies that pickfirst attempts to connect to the second backend once 966 // the happy eyeballs timer expires. 967 func (s) TestPickFirstLeaf_HappyEyeballs_TriggerConnectionDelay(t *testing.T) { 968 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 969 defer cancel() 970 971 originalTimer := pfinternal.TimeAfterFunc 972 defer func() { 973 pfinternal.TimeAfterFunc = originalTimer 974 }() 975 triggerTimer, timeAfter := mockTimer() 976 pfinternal.TimeAfterFunc = timeAfter 977 978 tmr := stats.NewTestMetricsRecorder() 979 dialer := testutils.NewBlockingDialer() 980 opts := []grpc.DialOption{ 981 grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, pickfirstleaf.Name)), 982 grpc.WithContextDialer(dialer.DialContext), 983 grpc.WithStatsHandler(tmr), 984 } 985 cc, rb, bm := setupPickFirstLeaf(t, 2, opts...) 986 addrs := bm.resolverAddrs() 987 holds := bm.holds(dialer) 988 rb.UpdateState(resolver.State{Addresses: addrs}) 989 cc.Connect() 990 991 testutils.AwaitState(ctx, t, cc, connectivity.Connecting) 992 993 // Verify that only the first server is contacted. 994 if holds[0].Wait(ctx) != true { 995 t.Fatalf("Timeout waiting for server %d with address %q to be contacted", 0, addrs[0]) 996 } 997 if holds[1].IsStarted() != false { 998 t.Fatalf("Server %d with address %q contacted unexpectedly", 1, addrs[1]) 999 } 1000 1001 // Make the happy eyeballs timer fire once and verify that the 1002 // second server is contacted. 1003 triggerTimer() 1004 if holds[1].Wait(ctx) != true { 1005 t.Fatalf("Timeout waiting for server %d with address %q to be contacted", 1, addrs[1]) 1006 } 1007 1008 // Get the connection attempt to the second server to succeed and verify 1009 // that the channel becomes READY. 1010 holds[1].Resume() 1011 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 1012 1013 // Only connection attempt successes in this test. 1014 if got, _ := tmr.Metric("grpc.lb.pick_first.connection_attempts_succeeded"); got != 1 { 1015 t.Errorf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.pick_first.connection_attempts_succeeded", got, 1) 1016 } 1017 if got, _ := tmr.Metric("grpc.lb.pick_first.connection_attempts_failed"); got != 0 { 1018 t.Errorf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.pick_first.connection_attempts_failed", got, 0) 1019 } 1020 if got, _ := tmr.Metric("grpc.lb.pick_first.disconnections"); got != 0 { 1021 t.Errorf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.pick_first.disconnections", got, 0) 1022 } 1023 } 1024 1025 // Test tests the pickfirst balancer by causing a SubConn to fail and then 1026 // jumping to the 3rd SubConn after the happy eyeballs timer expires. 1027 func (s) TestPickFirstLeaf_HappyEyeballs_TF_ThenTimerFires(t *testing.T) { 1028 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1029 defer cancel() 1030 1031 originalTimer := pfinternal.TimeAfterFunc 1032 defer func() { 1033 pfinternal.TimeAfterFunc = originalTimer 1034 }() 1035 triggerTimer, timeAfter := mockTimer() 1036 pfinternal.TimeAfterFunc = timeAfter 1037 1038 tmr := stats.NewTestMetricsRecorder() 1039 dialer := testutils.NewBlockingDialer() 1040 opts := []grpc.DialOption{ 1041 grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, pickfirstleaf.Name)), 1042 grpc.WithContextDialer(dialer.DialContext), 1043 grpc.WithStatsHandler(tmr), 1044 } 1045 cc, rb, bm := setupPickFirstLeaf(t, 3, opts...) 1046 addrs := bm.resolverAddrs() 1047 holds := bm.holds(dialer) 1048 rb.UpdateState(resolver.State{Addresses: addrs}) 1049 cc.Connect() 1050 1051 testutils.AwaitState(ctx, t, cc, connectivity.Connecting) 1052 1053 // Verify that only the first server is contacted. 1054 if holds[0].Wait(ctx) != true { 1055 t.Fatalf("Timeout waiting for server %d with address %q to be contacted", 0, addrs[0]) 1056 } 1057 if holds[1].IsStarted() != false { 1058 t.Fatalf("Server %d with address %q contacted unexpectedly", 1, addrs[1]) 1059 } 1060 if holds[2].IsStarted() != false { 1061 t.Fatalf("Server %d with address %q contacted unexpectedly", 2, addrs[2]) 1062 } 1063 1064 // First SubConn Fails. 1065 holds[0].Fail(fmt.Errorf("test error")) 1066 1067 // Verify that only the second server is contacted. 1068 if holds[1].Wait(ctx) != true { 1069 t.Fatalf("Timeout waiting for server %d with address %q to be contacted", 1, addrs[1]) 1070 } 1071 if got, _ := tmr.Metric("grpc.lb.pick_first.connection_attempts_failed"); got != 1 { 1072 t.Errorf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.pick_first.connection_attempts_failed", got, 1) 1073 } 1074 if holds[2].IsStarted() != false { 1075 t.Fatalf("Server %d with address %q contacted unexpectedly", 2, addrs[2]) 1076 } 1077 1078 // The happy eyeballs timer expires, pickfirst should stop waiting for 1079 // server[1] to report a failure/success and request the creation of a third 1080 // SubConn. 1081 triggerTimer() 1082 if holds[2].Wait(ctx) != true { 1083 t.Fatalf("Timeout waiting for server %d with address %q to be contacted", 2, addrs[2]) 1084 } 1085 1086 // Get the connection attempt to the second server to succeed and verify 1087 // that the channel becomes READY. 1088 holds[1].Resume() 1089 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 1090 1091 if got, _ := tmr.Metric("grpc.lb.pick_first.connection_attempts_succeeded"); got != 1 { 1092 t.Errorf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.pick_first.connection_attempts_succeeded", got, 1) 1093 } 1094 if got, _ := tmr.Metric("grpc.lb.pick_first.disconnections"); got != 0 { 1095 t.Errorf("Unexpected data for metric %v, got: %v, want: %v", "grpc.lb.pick_first.disconnections", got, 0) 1096 } 1097 } 1098 1099 func (s) TestPickFirstLeaf_InterleavingIPV4Preffered(t *testing.T) { 1100 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1101 defer cancel() 1102 cc := testutils.NewBalancerClientConn(t) 1103 bal := balancer.Get(pickfirstleaf.Name).Build(cc, balancer.BuildOptions{}) 1104 defer bal.Close() 1105 ccState := balancer.ClientConnState{ 1106 ResolverState: resolver.State{ 1107 Endpoints: []resolver.Endpoint{ 1108 {Addresses: []resolver.Address{{Addr: "1.1.1.1:1111"}}}, 1109 {Addresses: []resolver.Address{{Addr: "2.2.2.2:2"}}}, 1110 {Addresses: []resolver.Address{{Addr: "3.3.3.3:3"}}}, 1111 // IPv4-mapped IPv6 address, considered as an IPv4 for 1112 // interleaving. 1113 {Addresses: []resolver.Address{{Addr: "[::FFFF:192.168.0.1]:2222"}}}, 1114 {Addresses: []resolver.Address{{Addr: "[0001:0001:0001:0001:0001:0001:0001:0001]:8080"}}}, 1115 {Addresses: []resolver.Address{{Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:8080"}}}, 1116 {Addresses: []resolver.Address{{Addr: "[fe80::1%eth0]:3333"}}}, 1117 {Addresses: []resolver.Address{{Addr: "grpc.io:80"}}}, // not an IP. 1118 }, 1119 }, 1120 } 1121 if err := bal.UpdateClientConnState(ccState); err != nil { 1122 t.Fatalf("UpdateClientConnState(%v) returned error: %v", ccState, err) 1123 } 1124 1125 wantAddrs := []resolver.Address{ 1126 {Addr: "1.1.1.1:1111"}, 1127 {Addr: "[0001:0001:0001:0001:0001:0001:0001:0001]:8080"}, 1128 {Addr: "grpc.io:80"}, 1129 {Addr: "2.2.2.2:2"}, 1130 {Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:8080"}, 1131 {Addr: "3.3.3.3:3"}, 1132 {Addr: "[fe80::1%eth0]:3333"}, 1133 {Addr: "[::FFFF:192.168.0.1]:2222"}, 1134 } 1135 1136 gotAddrs, err := subConnAddresses(ctx, cc, 8) 1137 if err != nil { 1138 t.Fatalf("%v", err) 1139 } 1140 if diff := cmp.Diff(wantAddrs, gotAddrs, ignoreBalAttributesOpt); diff != "" { 1141 t.Errorf("SubConn creation order mismatch (-want +got):\n%s", diff) 1142 } 1143 } 1144 1145 func (s) TestPickFirstLeaf_InterleavingIPv6Preffered(t *testing.T) { 1146 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1147 defer cancel() 1148 cc := testutils.NewBalancerClientConn(t) 1149 bal := balancer.Get(pickfirstleaf.Name).Build(cc, balancer.BuildOptions{}) 1150 defer bal.Close() 1151 ccState := balancer.ClientConnState{ 1152 ResolverState: resolver.State{ 1153 Endpoints: []resolver.Endpoint{ 1154 {Addresses: []resolver.Address{{Addr: "[0001:0001:0001:0001:0001:0001:0001:0001]:8080"}}}, 1155 {Addresses: []resolver.Address{{Addr: "1.1.1.1:1111"}}}, 1156 {Addresses: []resolver.Address{{Addr: "2.2.2.2:2"}}}, 1157 {Addresses: []resolver.Address{{Addr: "3.3.3.3:3"}}}, 1158 {Addresses: []resolver.Address{{Addr: "[::FFFF:192.168.0.1]:2222"}}}, 1159 {Addresses: []resolver.Address{{Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:2222"}}}, 1160 {Addresses: []resolver.Address{{Addr: "[fe80::1%eth0]:3333"}}}, 1161 {Addresses: []resolver.Address{{Addr: "grpc.io:80"}}}, // not an IP. 1162 }, 1163 }, 1164 } 1165 if err := bal.UpdateClientConnState(ccState); err != nil { 1166 t.Fatalf("UpdateClientConnState(%v) returned error: %v", ccState, err) 1167 } 1168 1169 wantAddrs := []resolver.Address{ 1170 {Addr: "[0001:0001:0001:0001:0001:0001:0001:0001]:8080"}, 1171 {Addr: "1.1.1.1:1111"}, 1172 {Addr: "grpc.io:80"}, 1173 {Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:2222"}, 1174 {Addr: "2.2.2.2:2"}, 1175 {Addr: "[fe80::1%eth0]:3333"}, 1176 {Addr: "3.3.3.3:3"}, 1177 {Addr: "[::FFFF:192.168.0.1]:2222"}, 1178 } 1179 1180 gotAddrs, err := subConnAddresses(ctx, cc, 8) 1181 if err != nil { 1182 t.Fatalf("%v", err) 1183 } 1184 if diff := cmp.Diff(wantAddrs, gotAddrs, ignoreBalAttributesOpt); diff != "" { 1185 t.Errorf("SubConn creation order mismatch (-want +got):\n%s", diff) 1186 } 1187 } 1188 1189 func (s) TestPickFirstLeaf_InterleavingUnknownPreffered(t *testing.T) { 1190 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1191 defer cancel() 1192 cc := testutils.NewBalancerClientConn(t) 1193 bal := balancer.Get(pickfirstleaf.Name).Build(cc, balancer.BuildOptions{}) 1194 defer bal.Close() 1195 ccState := balancer.ClientConnState{ 1196 ResolverState: resolver.State{ 1197 Endpoints: []resolver.Endpoint{ 1198 {Addresses: []resolver.Address{{Addr: "grpc.io:80"}}}, // not an IP. 1199 {Addresses: []resolver.Address{{Addr: "1.1.1.1:1111"}}}, 1200 {Addresses: []resolver.Address{{Addr: "2.2.2.2:2"}}}, 1201 {Addresses: []resolver.Address{{Addr: "3.3.3.3:3"}}}, 1202 {Addresses: []resolver.Address{{Addr: "[::FFFF:192.168.0.1]:2222"}}}, 1203 {Addresses: []resolver.Address{{Addr: "[0001:0001:0001:0001:0001:0001:0001:0001]:8080"}}}, 1204 {Addresses: []resolver.Address{{Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:8080"}}}, 1205 {Addresses: []resolver.Address{{Addr: "[fe80::1%eth0]:3333"}}}, 1206 {Addresses: []resolver.Address{{Addr: "example.com:80"}}}, // not an IP. 1207 }, 1208 }, 1209 } 1210 if err := bal.UpdateClientConnState(ccState); err != nil { 1211 t.Fatalf("UpdateClientConnState(%v) returned error: %v", ccState, err) 1212 } 1213 1214 wantAddrs := []resolver.Address{ 1215 {Addr: "grpc.io:80"}, 1216 {Addr: "1.1.1.1:1111"}, 1217 {Addr: "[0001:0001:0001:0001:0001:0001:0001:0001]:8080"}, 1218 {Addr: "example.com:80"}, 1219 {Addr: "2.2.2.2:2"}, 1220 {Addr: "[0002:0002:0002:0002:0002:0002:0002:0002]:8080"}, 1221 {Addr: "3.3.3.3:3"}, 1222 {Addr: "[fe80::1%eth0]:3333"}, 1223 {Addr: "[::FFFF:192.168.0.1]:2222"}, 1224 } 1225 1226 gotAddrs, err := subConnAddresses(ctx, cc, 9) 1227 if err != nil { 1228 t.Fatalf("%v", err) 1229 } 1230 if diff := cmp.Diff(wantAddrs, gotAddrs, ignoreBalAttributesOpt); diff != "" { 1231 t.Errorf("SubConn creation order mismatch (-want +got):\n%s", diff) 1232 } 1233 } 1234 1235 // Test verifies that pickfirst balancer transitions to READY when the health 1236 // listener is enabled. Since client side health checking is not enabled in 1237 // the service config, the health listener will send a health update for READY 1238 // after registering the listener. 1239 func (s) TestPickFirstLeaf_HealthListenerEnabled(t *testing.T) { 1240 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1241 defer cancel() 1242 bf := stub.BalancerFuncs{ 1243 Init: func(bd *stub.BalancerData) { 1244 bd.Data = balancer.Get(pickfirstleaf.Name).Build(bd.ClientConn, bd.BuildOptions) 1245 }, 1246 Close: func(bd *stub.BalancerData) { 1247 bd.Data.(balancer.Balancer).Close() 1248 }, 1249 UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { 1250 ccs.ResolverState = pickfirstleaf.EnableHealthListener(ccs.ResolverState) 1251 return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) 1252 }, 1253 } 1254 1255 stub.Register(t.Name(), bf) 1256 svcCfg := fmt.Sprintf(`{ "loadBalancingConfig": [{%q: {}}] }`, t.Name()) 1257 backend := stubserver.StartTestService(t, nil) 1258 defer backend.Stop() 1259 opts := []grpc.DialOption{ 1260 grpc.WithTransportCredentials(insecure.NewCredentials()), 1261 grpc.WithDefaultServiceConfig(svcCfg), 1262 } 1263 cc, err := grpc.NewClient(backend.Address, opts...) 1264 if err != nil { 1265 t.Fatalf("grpc.NewClient(%q) failed: %v", backend.Address, err) 1266 1267 } 1268 defer cc.Close() 1269 1270 if err := pickfirst.CheckRPCsToBackend(ctx, cc, resolver.Address{Addr: backend.Address}); err != nil { 1271 t.Fatal(err) 1272 } 1273 } 1274 1275 // Test verifies that a health listener is not registered when pickfirst is not 1276 // under a petiole policy. 1277 func (s) TestPickFirstLeaf_HealthListenerNotEnabled(t *testing.T) { 1278 // Wrap the clientconn to intercept NewSubConn. 1279 // Capture the health list by wrapping the SC. 1280 // Wrap the picker to unwrap the SC. 1281 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1282 defer cancel() 1283 healthListenerCh := make(chan func(balancer.SubConnState)) 1284 1285 bf := stub.BalancerFuncs{ 1286 Init: func(bd *stub.BalancerData) { 1287 ccw := &healthListenerCapturingCCWrapper{ 1288 ClientConn: bd.ClientConn, 1289 healthListenerCh: healthListenerCh, 1290 subConnStateCh: make(chan balancer.SubConnState, 5), 1291 } 1292 bd.Data = balancer.Get(pickfirstleaf.Name).Build(ccw, bd.BuildOptions) 1293 }, 1294 Close: func(bd *stub.BalancerData) { 1295 bd.Data.(balancer.Balancer).Close() 1296 }, 1297 UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { 1298 // Functions like a non-petiole policy by not configuring the use 1299 // of health listeners. 1300 return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) 1301 }, 1302 } 1303 1304 stub.Register(t.Name(), bf) 1305 svcCfg := fmt.Sprintf(`{ "loadBalancingConfig": [{%q: {}}] }`, t.Name()) 1306 backend := stubserver.StartTestService(t, nil) 1307 defer backend.Stop() 1308 opts := []grpc.DialOption{ 1309 grpc.WithTransportCredentials(insecure.NewCredentials()), 1310 grpc.WithDefaultServiceConfig(svcCfg), 1311 } 1312 cc, err := grpc.NewClient(backend.Address, opts...) 1313 if err != nil { 1314 t.Fatalf("grpc.NewClient(%q) failed: %v", backend.Address, err) 1315 1316 } 1317 defer cc.Close() 1318 cc.Connect() 1319 1320 select { 1321 case <-healthListenerCh: 1322 t.Fatal("Health listener registered when not enabled.") 1323 case <-time.After(defaultTestShortTimeout): 1324 } 1325 1326 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 1327 } 1328 1329 // Test mocks the updates sent to the health listener and verifies that the 1330 // balancer correctly reports the health state once the SubConn's connectivity 1331 // state becomes READY. 1332 func (s) TestPickFirstLeaf_HealthUpdates(t *testing.T) { 1333 // Wrap the clientconn to intercept NewSubConn. 1334 // Capture the health list by wrapping the SC. 1335 // Wrap the picker to unwrap the SC. 1336 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1337 defer cancel() 1338 healthListenerCh := make(chan func(balancer.SubConnState)) 1339 scConnectivityStateCh := make(chan balancer.SubConnState, 5) 1340 1341 bf := stub.BalancerFuncs{ 1342 Init: func(bd *stub.BalancerData) { 1343 ccw := &healthListenerCapturingCCWrapper{ 1344 ClientConn: bd.ClientConn, 1345 healthListenerCh: healthListenerCh, 1346 subConnStateCh: scConnectivityStateCh, 1347 } 1348 bd.Data = balancer.Get(pickfirstleaf.Name).Build(ccw, bd.BuildOptions) 1349 }, 1350 Close: func(bd *stub.BalancerData) { 1351 bd.Data.(balancer.Balancer).Close() 1352 }, 1353 UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { 1354 ccs.ResolverState = pickfirstleaf.EnableHealthListener(ccs.ResolverState) 1355 return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) 1356 }, 1357 } 1358 1359 stub.Register(t.Name(), bf) 1360 svcCfg := fmt.Sprintf(`{ "loadBalancingConfig": [{%q: {}}] }`, t.Name()) 1361 backend := stubserver.StartTestService(t, nil) 1362 defer backend.Stop() 1363 opts := []grpc.DialOption{ 1364 grpc.WithTransportCredentials(insecure.NewCredentials()), 1365 grpc.WithDefaultServiceConfig(svcCfg), 1366 } 1367 cc, err := grpc.NewClient(backend.Address, opts...) 1368 if err != nil { 1369 t.Fatalf("grpc.NewClient(%q) failed: %v", backend.Address, err) 1370 1371 } 1372 defer cc.Close() 1373 cc.Connect() 1374 1375 var healthListener func(balancer.SubConnState) 1376 select { 1377 case healthListener = <-healthListenerCh: 1378 case <-ctx.Done(): 1379 t.Fatal("Context timed out waiting for health listener to be registered.") 1380 } 1381 1382 // Wait for the raw connectivity state to become READY. The LB policy should 1383 // wait for the health updates before transitioning the channel to READY. 1384 for { 1385 var scs balancer.SubConnState 1386 select { 1387 case scs = <-scConnectivityStateCh: 1388 case <-ctx.Done(): 1389 t.Fatal("Context timed out waiting for the SubConn connectivity state to become READY.") 1390 } 1391 if scs.ConnectivityState == connectivity.Ready { 1392 break 1393 } 1394 } 1395 1396 shortCtx, cancel := context.WithTimeout(ctx, defaultTestShortTimeout) 1397 defer cancel() 1398 testutils.AwaitNoStateChange(shortCtx, t, cc, connectivity.Connecting) 1399 1400 // The LB policy should update the channel state based on the health state. 1401 healthListener(balancer.SubConnState{ 1402 ConnectivityState: connectivity.TransientFailure, 1403 ConnectionError: fmt.Errorf("test health check failure"), 1404 }) 1405 testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure) 1406 1407 healthListener(balancer.SubConnState{ 1408 ConnectivityState: connectivity.Connecting, 1409 ConnectionError: balancer.ErrNoSubConnAvailable, 1410 }) 1411 testutils.AwaitState(ctx, t, cc, connectivity.Connecting) 1412 1413 healthListener(balancer.SubConnState{ 1414 ConnectivityState: connectivity.Ready, 1415 }) 1416 if err := pickfirst.CheckRPCsToBackend(ctx, cc, resolver.Address{Addr: backend.Address}); err != nil { 1417 t.Fatal(err) 1418 } 1419 1420 // When the health check fails, the channel should transition to TF. 1421 healthListener(balancer.SubConnState{ 1422 ConnectivityState: connectivity.TransientFailure, 1423 ConnectionError: fmt.Errorf("test health check failure"), 1424 }) 1425 testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure) 1426 } 1427 1428 // Tests the case where an address update received by the pick_first LB policy 1429 // differs in metadata which should be ignored by the LB policy. In this case, 1430 // the test verifies that new connections are not created when the address 1431 // update only changes the metadata. 1432 func (s) TestPickFirstLeaf_AddressUpdateWithMetadata(t *testing.T) { 1433 dialer := testutils.NewBlockingDialer() 1434 dopts := []grpc.DialOption{ 1435 grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, pickfirstleaf.Name)), 1436 grpc.WithContextDialer(dialer.DialContext), 1437 } 1438 cc, r, backends := setupPickFirstLeaf(t, 2, dopts...) 1439 1440 // Add a metadata to the addresses before pushing them to the pick_first LB 1441 // policy through the manual resolver. 1442 addrs := backends.resolverAddrs() 1443 for i := range addrs { 1444 addrs[i].Metadata = &metadata.MD{ 1445 "test-metadata-1": []string{fmt.Sprintf("%d", i)}, 1446 } 1447 } 1448 r.UpdateState(resolver.State{Addresses: addrs}) 1449 1450 // Ensure that RPCs succeed to the expected backend. 1451 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1452 defer cancel() 1453 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil { 1454 t.Fatal(err) 1455 } 1456 1457 // Create holds for each backend. This will be used to verify the connection 1458 // is not re-established. 1459 holds := backends.holds(dialer) 1460 1461 // Add metadata to the addresses before pushing them to the pick_first LB 1462 // policy through the manual resolver. Leave the order of the addresses 1463 // unchanged. 1464 for i := range addrs { 1465 addrs[i].Metadata = &metadata.MD{ 1466 "test-metadata-2": []string{fmt.Sprintf("%d", i)}, 1467 } 1468 } 1469 r.UpdateState(resolver.State{Addresses: addrs}) 1470 1471 // Ensure that no new connection is established. 1472 for i := range holds { 1473 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 1474 defer sCancel() 1475 if holds[i].Wait(sCtx) { 1476 t.Fatalf("Unexpected connection attempt to backend: %s", addrs[i]) 1477 } 1478 } 1479 1480 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil { 1481 t.Fatal(err) 1482 } 1483 1484 // Add metadata to the addresses before pushing them to the pick_first LB 1485 // policy through the manual resolver. Reverse of the order of addresses. 1486 for i := range addrs { 1487 addrs[i].Metadata = &metadata.MD{ 1488 "test-metadata-3": []string{fmt.Sprintf("%d", i)}, 1489 } 1490 } 1491 addrs[0], addrs[1] = addrs[1], addrs[0] 1492 r.UpdateState(resolver.State{Addresses: addrs}) 1493 1494 // Ensure that no new connection is established. 1495 for i := range holds { 1496 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 1497 defer sCancel() 1498 if holds[i].Wait(sCtx) { 1499 t.Fatalf("Unexpected connection attempt to backend: %s", addrs[i]) 1500 } 1501 } 1502 if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil { 1503 t.Fatal(err) 1504 } 1505 } 1506 1507 // healthListenerCapturingCCWrapper is used to capture the health listener so 1508 // that health updates can be mocked for testing. 1509 type healthListenerCapturingCCWrapper struct { 1510 balancer.ClientConn 1511 healthListenerCh chan func(balancer.SubConnState) 1512 subConnStateCh chan balancer.SubConnState 1513 } 1514 1515 func (ccw *healthListenerCapturingCCWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { 1516 oldListener := opts.StateListener 1517 opts.StateListener = func(scs balancer.SubConnState) { 1518 ccw.subConnStateCh <- scs 1519 if oldListener != nil { 1520 oldListener(scs) 1521 } 1522 } 1523 sc, err := ccw.ClientConn.NewSubConn(addrs, opts) 1524 if err != nil { 1525 return nil, err 1526 } 1527 return &healthListenerCapturingSCWrapper{ 1528 SubConn: sc, 1529 listenerCh: ccw.healthListenerCh, 1530 }, nil 1531 } 1532 1533 func (ccw *healthListenerCapturingCCWrapper) UpdateState(state balancer.State) { 1534 state.Picker = &unwrappingPicker{state.Picker} 1535 ccw.ClientConn.UpdateState(state) 1536 } 1537 1538 type healthListenerCapturingSCWrapper struct { 1539 balancer.SubConn 1540 listenerCh chan func(balancer.SubConnState) 1541 } 1542 1543 func (scw *healthListenerCapturingSCWrapper) RegisterHealthListener(listener func(balancer.SubConnState)) { 1544 scw.listenerCh <- listener 1545 } 1546 1547 // unwrappingPicker unwraps SubConns because the channel expects SubConns to be 1548 // addrConns. 1549 type unwrappingPicker struct { 1550 balancer.Picker 1551 } 1552 1553 func (pw *unwrappingPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) { 1554 pr, err := pw.Picker.Pick(info) 1555 if pr.SubConn != nil { 1556 pr.SubConn = pr.SubConn.(*healthListenerCapturingSCWrapper).SubConn 1557 } 1558 return pr, err 1559 } 1560 1561 // subConnAddresses makes the pickfirst balancer create the requested number of 1562 // SubConns by triggering transient failures. The function returns the 1563 // addresses of the created SubConns. 1564 func subConnAddresses(ctx context.Context, cc *testutils.BalancerClientConn, subConnCount int) ([]resolver.Address, error) { 1565 addresses := []resolver.Address{} 1566 for i := 0; i < subConnCount; i++ { 1567 select { 1568 case <-ctx.Done(): 1569 return nil, fmt.Errorf("test timed out after creating %d subchannels, want %d", i, subConnCount) 1570 case sc := <-cc.NewSubConnCh: 1571 if len(sc.Addresses) != 1 { 1572 return nil, fmt.Errorf("new subchannel created with %d addresses, want 1", len(sc.Addresses)) 1573 } 1574 addresses = append(addresses, sc.Addresses[0]) 1575 sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 1576 sc.UpdateState(balancer.SubConnState{ 1577 ConnectivityState: connectivity.TransientFailure, 1578 }) 1579 } 1580 } 1581 return addresses, nil 1582 } 1583 1584 // stateStoringBalancer stores the state of the SubConns being created. 1585 type stateStoringBalancer struct { 1586 balancer.Balancer 1587 mu sync.Mutex 1588 scStates []*scState 1589 } 1590 1591 func (b *stateStoringBalancer) Close() { 1592 b.Balancer.Close() 1593 } 1594 1595 func (b *stateStoringBalancer) ExitIdle() { 1596 if ib, ok := b.Balancer.(balancer.ExitIdler); ok { 1597 ib.ExitIdle() 1598 } 1599 } 1600 1601 type stateStoringBalancerBuilder struct { 1602 balancer chan *stateStoringBalancer 1603 } 1604 1605 func (b *stateStoringBalancerBuilder) Name() string { 1606 return stateStoringBalancerName 1607 } 1608 1609 func (b *stateStoringBalancerBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer { 1610 bal := &stateStoringBalancer{} 1611 bal.Balancer = balancer.Get(pickfirstleaf.Name).Build(&stateStoringCCWrapper{cc, bal}, opts) 1612 b.balancer <- bal 1613 return bal 1614 } 1615 1616 func (b *stateStoringBalancer) subConnStates() []scState { 1617 b.mu.Lock() 1618 defer b.mu.Unlock() 1619 ret := []scState{} 1620 for _, s := range b.scStates { 1621 ret = append(ret, *s) 1622 } 1623 return ret 1624 } 1625 1626 func (b *stateStoringBalancer) addSCState(state *scState) { 1627 b.mu.Lock() 1628 b.scStates = append(b.scStates, state) 1629 b.mu.Unlock() 1630 } 1631 1632 type stateStoringCCWrapper struct { 1633 balancer.ClientConn 1634 b *stateStoringBalancer 1635 } 1636 1637 func (ccw *stateStoringCCWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { 1638 oldListener := opts.StateListener 1639 scs := &scState{ 1640 State: connectivity.Idle, 1641 Addrs: addrs, 1642 } 1643 ccw.b.addSCState(scs) 1644 opts.StateListener = func(s balancer.SubConnState) { 1645 ccw.b.mu.Lock() 1646 scs.State = s.ConnectivityState 1647 ccw.b.mu.Unlock() 1648 oldListener(s) 1649 } 1650 return ccw.ClientConn.NewSubConn(addrs, opts) 1651 } 1652 1653 type scState struct { 1654 State connectivity.State 1655 Addrs []resolver.Address 1656 } 1657 1658 type backendManager struct { 1659 backends []*testServer 1660 } 1661 1662 func (b *backendManager) stopAllExcept(index int) { 1663 for idx, b := range b.backends { 1664 if idx != index { 1665 b.stop() 1666 } 1667 } 1668 } 1669 1670 // resolverAddrs returns a list of resolver addresses for the stub server 1671 // backends. Useful when pushing addresses to the manual resolver. 1672 func (b *backendManager) resolverAddrs() []resolver.Address { 1673 addrs := make([]resolver.Address, len(b.backends)) 1674 for i, backend := range b.backends { 1675 addrs[i] = resolver.Address{Addr: backend.Address} 1676 } 1677 return addrs 1678 } 1679 1680 func (b *backendManager) holds(dialer *testutils.BlockingDialer) []*testutils.Hold { 1681 holds := []*testutils.Hold{} 1682 for _, addr := range b.resolverAddrs() { 1683 holds = append(holds, dialer.Hold(addr.Addr)) 1684 } 1685 return holds 1686 } 1687 1688 type ccStateSubscriber struct { 1689 mu sync.Mutex 1690 states []connectivity.State 1691 } 1692 1693 // transitions returns all the states that ccStateSubscriber recorded. 1694 // Without this a race condition occurs when the test compares the states 1695 // and the subscriber at the same time receives a connectivity.Shutdown. 1696 func (c *ccStateSubscriber) transitions() []connectivity.State { 1697 c.mu.Lock() 1698 defer c.mu.Unlock() 1699 return c.states 1700 } 1701 1702 func (c *ccStateSubscriber) OnMessage(msg any) { 1703 c.mu.Lock() 1704 defer c.mu.Unlock() 1705 c.states = append(c.states, msg.(connectivity.State)) 1706 } 1707 1708 // mockTimer returns a fake timeAfterFunc that will not trigger automatically. 1709 // It returns a function that can be called to manually trigger the execution 1710 // of the scheduled callback. 1711 func mockTimer() (triggerFunc func(), timerFunc func(_ time.Duration, f func()) func()) { 1712 timerCh := make(chan struct{}) 1713 triggerFunc = func() { 1714 timerCh <- struct{}{} 1715 } 1716 return triggerFunc, func(_ time.Duration, f func()) func() { 1717 stopCh := make(chan struct{}) 1718 go func() { 1719 select { 1720 case <-timerCh: 1721 f() 1722 case <-stopCh: 1723 } 1724 }() 1725 return sync.OnceFunc(func() { 1726 close(stopCh) 1727 }) 1728 } 1729 }