gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/xds/internal/balancer/clusterresolver/eds_impl_test.go (about) 1 /* 2 * Copyright 2019 gRPC authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package clusterresolver 18 19 import ( 20 "context" 21 "fmt" 22 "net/url" 23 "sort" 24 "testing" 25 "time" 26 27 corepb "gitee.com/ks-custle/core-gm/go-control-plane/envoy/api/v2/core" 28 "gitee.com/ks-custle/core-gm/grpc/balancer" 29 "gitee.com/ks-custle/core-gm/grpc/balancer/weightedtarget" 30 "gitee.com/ks-custle/core-gm/grpc/connectivity" 31 "gitee.com/ks-custle/core-gm/grpc/internal/balancergroup" 32 internalserviceconfig "gitee.com/ks-custle/core-gm/grpc/internal/serviceconfig" 33 "gitee.com/ks-custle/core-gm/grpc/internal/testutils" 34 "gitee.com/ks-custle/core-gm/grpc/resolver" 35 "gitee.com/ks-custle/core-gm/grpc/xds/internal/balancer/clusterimpl" 36 "gitee.com/ks-custle/core-gm/grpc/xds/internal/balancer/priority" 37 xdstestutils "gitee.com/ks-custle/core-gm/grpc/xds/internal/testutils" 38 "gitee.com/ks-custle/core-gm/grpc/xds/internal/testutils/fakeclient" 39 "gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient" 40 "gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource" 41 "github.com/google/go-cmp/cmp" 42 ) 43 44 var ( 45 testClusterNames = []string{"test-cluster-1", "test-cluster-2"} 46 testSubZones = []string{"I", "II", "III", "IV"} 47 testEndpointAddrs []string 48 ) 49 50 const testBackendAddrsCount = 12 51 52 func init() { 53 for i := 0; i < testBackendAddrsCount; i++ { 54 testEndpointAddrs = append(testEndpointAddrs, fmt.Sprintf("%d.%d.%d.%d:%d", i, i, i, i, i)) 55 } 56 balancergroup.DefaultSubBalancerCloseTimeout = time.Millisecond 57 clusterimpl.NewRandomWRR = testutils.NewTestWRR 58 weightedtarget.NewRandomWRR = testutils.NewTestWRR 59 balancergroup.DefaultSubBalancerCloseTimeout = time.Millisecond * 100 60 } 61 62 func setupTestEDS(t *testing.T, initChild *internalserviceconfig.BalancerConfig) (balancer.Balancer, *testutils.TestClientConn, *fakeclient.Client, func()) { 63 xdsC := fakeclient.NewClientWithName(testBalancerNameFooBar) 64 cc := testutils.NewTestClientConn(t) 65 builder := balancer.Get(Name) 66 // Endpoint is deprecated, use URL.Path instead. 67 //edsb := builder.Build(cc, balancer.BuildOptions{Target: resolver.Target{Endpoint: testEDSServcie}}) 68 edsb := builder.Build(cc, balancer.BuildOptions{Target: resolver.Target{URL: url.URL{Path: "/" + testEDSServcie}}}) 69 if edsb == nil { 70 t.Fatalf("builder.Build(%s) failed and returned nil", Name) 71 } 72 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 73 defer cancel() 74 if err := edsb.UpdateClientConnState(balancer.ClientConnState{ 75 ResolverState: xdsclient.SetClient(resolver.State{}, xdsC), 76 BalancerConfig: &LBConfig{ 77 DiscoveryMechanisms: []DiscoveryMechanism{{ 78 Cluster: testClusterName, 79 Type: DiscoveryMechanismTypeEDS, 80 }}, 81 }, 82 }); err != nil { 83 edsb.Close() 84 xdsC.Close() 85 t.Fatal(err) 86 } 87 if _, err := xdsC.WaitForWatchEDS(ctx); err != nil { 88 edsb.Close() 89 xdsC.Close() 90 t.Fatalf("xdsClient.WatchEndpoints failed with error: %v", err) 91 } 92 return edsb, cc, xdsC, func() { 93 edsb.Close() 94 xdsC.Close() 95 } 96 } 97 98 // One locality 99 // - add backend 100 // - remove backend 101 // - replace backend 102 // - change drop rate 103 func (s) TestEDS_OneLocality(t *testing.T) { 104 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 105 defer cleanup() 106 107 // One locality with one backend. 108 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 109 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 110 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 111 112 sc1 := <-cc.NewSubConnCh 113 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 114 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 115 116 // Pick with only the first backend. 117 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil { 118 t.Fatal(err) 119 } 120 121 // The same locality, add one more backend. 122 clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 123 clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:2], nil) 124 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil) 125 126 sc2 := <-cc.NewSubConnCh 127 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 128 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 129 130 // Test roundrobin with two subconns. 131 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1, sc2}); err != nil { 132 t.Fatal(err) 133 } 134 135 // The same locality, delete first backend. 136 clab3 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 137 clab3.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[1:2], nil) 138 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab3.Build()), nil) 139 140 scToRemove := <-cc.RemoveSubConnCh 141 if !cmp.Equal(scToRemove, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 142 t.Fatalf("RemoveSubConn, want %v, got %v", sc1, scToRemove) 143 } 144 edsb.UpdateSubConnState(scToRemove, balancer.SubConnState{ConnectivityState: connectivity.Shutdown}) 145 146 // Test pick with only the second subconn. 147 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil { 148 t.Fatal(err) 149 } 150 151 // The same locality, replace backend. 152 clab4 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 153 clab4.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[2:3], nil) 154 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab4.Build()), nil) 155 156 sc3 := <-cc.NewSubConnCh 157 edsb.UpdateSubConnState(sc3, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 158 edsb.UpdateSubConnState(sc3, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 159 scToRemove = <-cc.RemoveSubConnCh 160 if !cmp.Equal(scToRemove, sc2, cmp.AllowUnexported(testutils.TestSubConn{})) { 161 t.Fatalf("RemoveSubConn, want %v, got %v", sc2, scToRemove) 162 } 163 edsb.UpdateSubConnState(scToRemove, balancer.SubConnState{ConnectivityState: connectivity.Shutdown}) 164 165 // Test pick with only the third subconn. 166 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc3}); err != nil { 167 t.Fatal(err) 168 } 169 170 // The same locality, different drop rate, dropping 50%. 171 clab5 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], map[string]uint32{"test-drop": 50}) 172 clab5.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[2:3], nil) 173 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab5.Build()), nil) 174 175 // Picks with drops. 176 if err := testPickerFromCh(cc.NewPickerCh, func(p balancer.Picker) error { 177 for i := 0; i < 100; i++ { 178 _, err := p.Pick(balancer.PickInfo{}) 179 // TODO: the dropping algorithm needs a design. When the dropping algorithm 180 // is fixed, this test also needs fix. 181 if i%2 == 0 && err == nil { 182 return fmt.Errorf("%d - the even number picks should be drops, got error <nil>", i) 183 } else if i%2 != 0 && err != nil { 184 return fmt.Errorf("%d - the odd number picks should be non-drops, got error %v", i, err) 185 } 186 } 187 return nil 188 }); err != nil { 189 t.Fatal(err) 190 } 191 192 // The same locality, remove drops. 193 clab6 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 194 clab6.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[2:3], nil) 195 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab6.Build()), nil) 196 197 // Pick without drops. 198 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc3}); err != nil { 199 t.Fatal(err) 200 } 201 } 202 203 // 2 locality 204 // - start with 2 locality 205 // - add locality 206 // - remove locality 207 // - address change for the <not-the-first> locality 208 // - update locality weight 209 func (s) TestEDS_TwoLocalities(t *testing.T) { 210 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 211 defer cleanup() 212 213 // Two localities, each with one backend. 214 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 215 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 216 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 217 sc1 := <-cc.NewSubConnCh 218 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 219 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 220 221 // Add the second locality later to make sure sc2 belongs to the second 222 // locality. Otherwise the test is flaky because of a map is used in EDS to 223 // keep localities. 224 clab1.AddLocality(testSubZones[1], 1, 0, testEndpointAddrs[1:2], nil) 225 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 226 sc2 := <-cc.NewSubConnCh 227 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 228 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 229 230 // Test roundrobin with two subconns. 231 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1, sc2}); err != nil { 232 t.Fatal(err) 233 } 234 235 // Add another locality, with one backend. 236 clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 237 clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 238 clab2.AddLocality(testSubZones[1], 1, 0, testEndpointAddrs[1:2], nil) 239 clab2.AddLocality(testSubZones[2], 1, 0, testEndpointAddrs[2:3], nil) 240 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil) 241 242 sc3 := <-cc.NewSubConnCh 243 edsb.UpdateSubConnState(sc3, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 244 edsb.UpdateSubConnState(sc3, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 245 246 // Test roundrobin with three subconns. 247 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1, sc2, sc3}); err != nil { 248 t.Fatal(err) 249 } 250 251 // Remove first locality. 252 clab3 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 253 clab3.AddLocality(testSubZones[1], 1, 0, testEndpointAddrs[1:2], nil) 254 clab3.AddLocality(testSubZones[2], 1, 0, testEndpointAddrs[2:3], nil) 255 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab3.Build()), nil) 256 257 scToRemove := <-cc.RemoveSubConnCh 258 if !cmp.Equal(scToRemove, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 259 t.Fatalf("RemoveSubConn, want %v, got %v", sc1, scToRemove) 260 } 261 edsb.UpdateSubConnState(scToRemove, balancer.SubConnState{ConnectivityState: connectivity.Shutdown}) 262 263 // Test pick with two subconns (without the first one). 264 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2, sc3}); err != nil { 265 t.Fatal(err) 266 } 267 268 // Add a backend to the last locality. 269 clab4 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 270 clab4.AddLocality(testSubZones[1], 1, 0, testEndpointAddrs[1:2], nil) 271 clab4.AddLocality(testSubZones[2], 1, 0, testEndpointAddrs[2:4], nil) 272 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab4.Build()), nil) 273 274 sc4 := <-cc.NewSubConnCh 275 edsb.UpdateSubConnState(sc4, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 276 edsb.UpdateSubConnState(sc4, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 277 278 // Test pick with two subconns (without the first one). 279 // 280 // Locality-1 will be picked twice, and locality-2 will be picked twice. 281 // Locality-1 contains only sc2, locality-2 contains sc3 and sc4. So expect 282 // two sc2's and sc3, sc4. 283 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2, sc2, sc3, sc4}); err != nil { 284 t.Fatal(err) 285 } 286 287 // Change weight of the locality[1]. 288 clab5 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 289 clab5.AddLocality(testSubZones[1], 2, 0, testEndpointAddrs[1:2], nil) 290 clab5.AddLocality(testSubZones[2], 1, 0, testEndpointAddrs[2:4], nil) 291 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab5.Build()), nil) 292 293 // Test pick with two subconns different locality weight. 294 // 295 // Locality-1 will be picked four times, and locality-2 will be picked twice 296 // (weight 2 and 1). Locality-1 contains only sc2, locality-2 contains sc3 and 297 // sc4. So expect four sc2's and sc3, sc4. 298 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2, sc2, sc2, sc2, sc3, sc4}); err != nil { 299 t.Fatal(err) 300 } 301 302 // Change weight of the locality[1] to 0, it should never be picked. 303 clab6 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 304 clab6.AddLocality(testSubZones[1], 0, 0, testEndpointAddrs[1:2], nil) 305 clab6.AddLocality(testSubZones[2], 1, 0, testEndpointAddrs[2:4], nil) 306 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab6.Build()), nil) 307 308 // Changing weight of locality[1] to 0 caused it to be removed. It's subconn 309 // should also be removed. 310 // 311 // NOTE: this is because we handle locality with weight 0 same as the 312 // locality doesn't exist. If this changes in the future, this removeSubConn 313 // behavior will also change. 314 scToRemove2 := <-cc.RemoveSubConnCh 315 if !cmp.Equal(scToRemove2, sc2, cmp.AllowUnexported(testutils.TestSubConn{})) { 316 t.Fatalf("RemoveSubConn, want %v, got %v", sc2, scToRemove2) 317 } 318 319 // Test pick with two subconns different locality weight. 320 // 321 // Locality-1 will be not be picked, and locality-2 will be picked. 322 // Locality-2 contains sc3 and sc4. So expect sc3, sc4. 323 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc3, sc4}); err != nil { 324 t.Fatal(err) 325 } 326 } 327 328 // The EDS balancer gets EDS resp with unhealthy endpoints. Test that only 329 // healthy ones are used. 330 func (s) TestEDS_EndpointsHealth(t *testing.T) { 331 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 332 defer cleanup() 333 334 // Two localities, each 3 backend, one Healthy, one Unhealthy, one Unknown. 335 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 336 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:6], &xdstestutils.AddLocalityOptions{ 337 Health: []corepb.HealthStatus{ 338 corepb.HealthStatus_HEALTHY, 339 corepb.HealthStatus_UNHEALTHY, 340 corepb.HealthStatus_UNKNOWN, 341 corepb.HealthStatus_DRAINING, 342 corepb.HealthStatus_TIMEOUT, 343 corepb.HealthStatus_DEGRADED, 344 }, 345 }) 346 clab1.AddLocality(testSubZones[1], 1, 0, testEndpointAddrs[6:12], &xdstestutils.AddLocalityOptions{ 347 Health: []corepb.HealthStatus{ 348 corepb.HealthStatus_HEALTHY, 349 corepb.HealthStatus_UNHEALTHY, 350 corepb.HealthStatus_UNKNOWN, 351 corepb.HealthStatus_DRAINING, 352 corepb.HealthStatus_TIMEOUT, 353 corepb.HealthStatus_DEGRADED, 354 }, 355 }) 356 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 357 358 var ( 359 readySCs []balancer.SubConn 360 newSubConnAddrStrs []string 361 ) 362 for i := 0; i < 4; i++ { 363 addr := <-cc.NewSubConnAddrsCh 364 newSubConnAddrStrs = append(newSubConnAddrStrs, addr[0].Addr) 365 sc := <-cc.NewSubConnCh 366 edsb.UpdateSubConnState(sc, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 367 edsb.UpdateSubConnState(sc, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 368 readySCs = append(readySCs, sc) 369 } 370 371 wantNewSubConnAddrStrs := []string{ 372 testEndpointAddrs[0], 373 testEndpointAddrs[2], 374 testEndpointAddrs[6], 375 testEndpointAddrs[8], 376 } 377 sortStrTrans := cmp.Transformer("Sort", func(in []string) []string { 378 out := append([]string(nil), in...) // Copy input to avoid mutating it. 379 sort.Strings(out) 380 return out 381 }) 382 if !cmp.Equal(newSubConnAddrStrs, wantNewSubConnAddrStrs, sortStrTrans) { 383 t.Fatalf("want newSubConn with address %v, got %v", wantNewSubConnAddrStrs, newSubConnAddrStrs) 384 } 385 386 // There should be exactly 4 new SubConns. Check to make sure there's no 387 // more subconns being created. 388 select { 389 case <-cc.NewSubConnCh: 390 t.Fatalf("Got unexpected new subconn") 391 case <-time.After(time.Microsecond * 100): 392 } 393 394 // Test roundrobin with the subconns. 395 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, readySCs); err != nil { 396 t.Fatal(err) 397 } 398 } 399 400 // TestEDS_EmptyUpdate covers the cases when eds impl receives an empty update. 401 // 402 // It should send an error picker with transient failure to the parent. 403 func (s) TestEDS_EmptyUpdate(t *testing.T) { 404 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 405 defer cleanup() 406 407 const cacheTimeout = 100 * time.Microsecond 408 oldCacheTimeout := balancergroup.DefaultSubBalancerCloseTimeout 409 balancergroup.DefaultSubBalancerCloseTimeout = cacheTimeout 410 defer func() { balancergroup.DefaultSubBalancerCloseTimeout = oldCacheTimeout }() 411 412 // The first update is an empty update. 413 xdsC.InvokeWatchEDSCallback("", xdsresource.EndpointsUpdate{}, nil) 414 // Pick should fail with transient failure, and all priority removed error. 415 if err := testErrPickerFromCh(cc.NewPickerCh, priority.ErrAllPrioritiesRemoved); err != nil { 416 t.Fatal(err) 417 } 418 419 // One locality with one backend. 420 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 421 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 422 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 423 424 sc1 := <-cc.NewSubConnCh 425 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 426 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 427 428 // Pick with only the first backend. 429 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil { 430 t.Fatal(err) 431 } 432 433 xdsC.InvokeWatchEDSCallback("", xdsresource.EndpointsUpdate{}, nil) 434 // Pick should fail with transient failure, and all priority removed error. 435 if err := testErrPickerFromCh(cc.NewPickerCh, priority.ErrAllPrioritiesRemoved); err != nil { 436 t.Fatal(err) 437 } 438 439 // Wait for the old SubConn to be removed (which happens when the child 440 // policy is closed), so a new update would trigger a new SubConn (we need 441 // this new SubConn to tell if the next picker is newly created). 442 scToRemove := <-cc.RemoveSubConnCh 443 if !cmp.Equal(scToRemove, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 444 t.Fatalf("RemoveSubConn, want %v, got %v", sc1, scToRemove) 445 } 446 edsb.UpdateSubConnState(scToRemove, balancer.SubConnState{ConnectivityState: connectivity.Shutdown}) 447 448 // Handle another update with priorities and localities. 449 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 450 451 sc2 := <-cc.NewSubConnCh 452 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 453 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 454 455 // Pick with only the first backend. 456 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil { 457 t.Fatal(err) 458 } 459 } 460 461 func (s) TestEDS_CircuitBreaking(t *testing.T) { 462 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 463 defer cleanup() 464 465 var maxRequests uint32 = 50 466 if err := edsb.UpdateClientConnState(balancer.ClientConnState{ 467 BalancerConfig: &LBConfig{ 468 DiscoveryMechanisms: []DiscoveryMechanism{{ 469 Cluster: testClusterName, 470 MaxConcurrentRequests: &maxRequests, 471 Type: DiscoveryMechanismTypeEDS, 472 }}, 473 }, 474 }); err != nil { 475 t.Fatal(err) 476 } 477 478 // One locality with one backend. 479 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 480 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 481 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 482 sc1 := <-cc.NewSubConnCh 483 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 484 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 485 486 // Picks with drops. 487 dones := []func(){} 488 p := <-cc.NewPickerCh 489 for i := 0; i < 100; i++ { 490 pr, err := p.Pick(balancer.PickInfo{}) 491 if i < 50 && err != nil { 492 t.Errorf("The first 50%% picks should be non-drops, got error %v", err) 493 } else if i > 50 && err == nil { 494 t.Errorf("The second 50%% picks should be drops, got error <nil>") 495 } 496 dones = append(dones, func() { 497 if pr.Done != nil { 498 pr.Done(balancer.DoneInfo{}) 499 } 500 }) 501 } 502 503 for _, done := range dones { 504 done() 505 } 506 dones = []func(){} 507 508 // Pick without drops. 509 for i := 0; i < 50; i++ { 510 pr, err := p.Pick(balancer.PickInfo{}) 511 if err != nil { 512 t.Errorf("The third 50%% picks should be non-drops, got error %v", err) 513 } 514 dones = append(dones, func() { 515 if pr.Done != nil { 516 pr.Done(balancer.DoneInfo{}) 517 } 518 }) 519 } 520 521 // Without this, future tests with the same service name will fail. 522 for _, done := range dones { 523 done() 524 } 525 526 // Send another update, with only circuit breaking update (and no picker 527 // update afterwards). Make sure the new picker uses the new configs. 528 var maxRequests2 uint32 = 10 529 if err := edsb.UpdateClientConnState(balancer.ClientConnState{ 530 BalancerConfig: &LBConfig{ 531 DiscoveryMechanisms: []DiscoveryMechanism{{ 532 Cluster: testClusterName, 533 MaxConcurrentRequests: &maxRequests2, 534 Type: DiscoveryMechanismTypeEDS, 535 }}, 536 }, 537 }); err != nil { 538 t.Fatal(err) 539 } 540 541 // Picks with drops. 542 dones = []func(){} 543 p2 := <-cc.NewPickerCh 544 for i := 0; i < 100; i++ { 545 pr, err := p2.Pick(balancer.PickInfo{}) 546 if i < 10 && err != nil { 547 t.Errorf("The first 10%% picks should be non-drops, got error %v", err) 548 } else if i > 10 && err == nil { 549 t.Errorf("The next 90%% picks should be drops, got error <nil>") 550 } 551 dones = append(dones, func() { 552 if pr.Done != nil { 553 pr.Done(balancer.DoneInfo{}) 554 } 555 }) 556 } 557 558 for _, done := range dones { 559 done() 560 } 561 dones = []func(){} 562 563 // Pick without drops. 564 for i := 0; i < 10; i++ { 565 pr, err := p2.Pick(balancer.PickInfo{}) 566 if err != nil { 567 t.Errorf("The next 10%% picks should be non-drops, got error %v", err) 568 } 569 dones = append(dones, func() { 570 if pr.Done != nil { 571 pr.Done(balancer.DoneInfo{}) 572 } 573 }) 574 } 575 576 // Without this, future tests with the same service name will fail. 577 for _, done := range dones { 578 done() 579 } 580 }