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  }