github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/clusterimpl/balancer_test.go (about)

     1  /*
     2   *
     3   * Copyright 2020 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package clusterimpl
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/google/go-cmp/cmp"
    30  	"github.com/google/go-cmp/cmp/cmpopts"
    31  	"github.com/hxx258456/ccgo/grpc/balancer"
    32  	"github.com/hxx258456/ccgo/grpc/balancer/base"
    33  	"github.com/hxx258456/ccgo/grpc/balancer/roundrobin"
    34  	"github.com/hxx258456/ccgo/grpc/connectivity"
    35  	"github.com/hxx258456/ccgo/grpc/internal"
    36  	"github.com/hxx258456/ccgo/grpc/internal/balancer/stub"
    37  	"github.com/hxx258456/ccgo/grpc/internal/grpctest"
    38  	internalserviceconfig "github.com/hxx258456/ccgo/grpc/internal/serviceconfig"
    39  	"github.com/hxx258456/ccgo/grpc/internal/testutils"
    40  	"github.com/hxx258456/ccgo/grpc/resolver"
    41  	xdsinternal "github.com/hxx258456/ccgo/grpc/xds/internal"
    42  	"github.com/hxx258456/ccgo/grpc/xds/internal/testutils/fakeclient"
    43  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient"
    44  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/load"
    45  )
    46  
    47  const (
    48  	defaultTestTimeout      = 1 * time.Second
    49  	defaultShortTestTimeout = 100 * time.Microsecond
    50  
    51  	testClusterName   = "test-cluster"
    52  	testServiceName   = "test-eds-service"
    53  	testLRSServerName = "test-lrs-name"
    54  )
    55  
    56  var (
    57  	testBackendAddrs = []resolver.Address{
    58  		{Addr: "1.1.1.1:1"},
    59  	}
    60  
    61  	cmpOpts = cmp.Options{
    62  		cmpopts.EquateEmpty(),
    63  		cmpopts.IgnoreFields(load.Data{}, "ReportInterval"),
    64  	}
    65  )
    66  
    67  type s struct {
    68  	grpctest.Tester
    69  }
    70  
    71  func Test(t *testing.T) {
    72  	grpctest.RunSubTests(t, s{})
    73  }
    74  
    75  func subConnFromPicker(p balancer.Picker) func() balancer.SubConn {
    76  	return func() balancer.SubConn {
    77  		scst, _ := p.Pick(balancer.PickInfo{})
    78  		return scst.SubConn
    79  	}
    80  }
    81  
    82  func init() {
    83  	NewRandomWRR = testutils.NewTestWRR
    84  }
    85  
    86  // TestDropByCategory verifies that the balancer correctly drops the picks, and
    87  // that the drops are reported.
    88  func (s) TestDropByCategory(t *testing.T) {
    89  	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
    90  	xdsC := fakeclient.NewClient()
    91  	defer xdsC.Close()
    92  
    93  	builder := balancer.Get(Name)
    94  	cc := testutils.NewTestClientConn(t)
    95  	b := builder.Build(cc, balancer.BuildOptions{})
    96  	defer b.Close()
    97  
    98  	const (
    99  		dropReason      = "test-dropping-category"
   100  		dropNumerator   = 1
   101  		dropDenominator = 2
   102  	)
   103  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   104  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   105  		BalancerConfig: &LBConfig{
   106  			Cluster:                 testClusterName,
   107  			EDSServiceName:          testServiceName,
   108  			LoadReportingServerName: newString(testLRSServerName),
   109  			DropCategories: []DropConfig{{
   110  				Category:           dropReason,
   111  				RequestsPerMillion: million * dropNumerator / dropDenominator,
   112  			}},
   113  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   114  				Name: roundrobin.Name,
   115  			},
   116  		},
   117  	}); err != nil {
   118  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   119  	}
   120  
   121  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   122  	defer cancel()
   123  
   124  	got, err := xdsC.WaitForReportLoad(ctx)
   125  	if err != nil {
   126  		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
   127  	}
   128  	if got.Server != testLRSServerName {
   129  		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerName)
   130  	}
   131  
   132  	sc1 := <-cc.NewSubConnCh
   133  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   134  	// This should get the connecting picker.
   135  	p0 := <-cc.NewPickerCh
   136  	for i := 0; i < 10; i++ {
   137  		_, err := p0.Pick(balancer.PickInfo{})
   138  		if err != balancer.ErrNoSubConnAvailable {
   139  			t.Fatalf("picker.Pick, got _,%v, want Err=%v", err, balancer.ErrNoSubConnAvailable)
   140  		}
   141  	}
   142  
   143  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   144  	// Test pick with one backend.
   145  	p1 := <-cc.NewPickerCh
   146  	const rpcCount = 20
   147  	for i := 0; i < rpcCount; i++ {
   148  		gotSCSt, err := p1.Pick(balancer.PickInfo{})
   149  		// Even RPCs are dropped.
   150  		if i%2 == 0 {
   151  			if err == nil || !strings.Contains(err.Error(), "dropped") {
   152  				t.Fatalf("pick.Pick, got %v, %v, want error RPC dropped", gotSCSt, err)
   153  			}
   154  			continue
   155  		}
   156  		if err != nil || !cmp.Equal(gotSCSt.SubConn, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) {
   157  			t.Fatalf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
   158  		}
   159  		if gotSCSt.Done != nil {
   160  			gotSCSt.Done(balancer.DoneInfo{})
   161  		}
   162  	}
   163  
   164  	// Dump load data from the store and compare with expected counts.
   165  	loadStore := xdsC.LoadStore()
   166  	if loadStore == nil {
   167  		t.Fatal("loadStore is nil in xdsClient")
   168  	}
   169  	const dropCount = rpcCount * dropNumerator / dropDenominator
   170  	wantStatsData0 := []*load.Data{{
   171  		Cluster:    testClusterName,
   172  		Service:    testServiceName,
   173  		TotalDrops: dropCount,
   174  		Drops:      map[string]uint64{dropReason: dropCount},
   175  		LocalityStats: map[string]load.LocalityData{
   176  			assertString(xdsinternal.LocalityID{}.ToString): {RequestStats: load.RequestData{Succeeded: rpcCount - dropCount}},
   177  		},
   178  	}}
   179  
   180  	gotStatsData0 := loadStore.Stats([]string{testClusterName})
   181  	if diff := cmp.Diff(gotStatsData0, wantStatsData0, cmpOpts); diff != "" {
   182  		t.Fatalf("got unexpected reports, diff (-got, +want): %v", diff)
   183  	}
   184  
   185  	// Send an update with new drop configs.
   186  	const (
   187  		dropReason2      = "test-dropping-category-2"
   188  		dropNumerator2   = 1
   189  		dropDenominator2 = 4
   190  	)
   191  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   192  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   193  		BalancerConfig: &LBConfig{
   194  			Cluster:                 testClusterName,
   195  			EDSServiceName:          testServiceName,
   196  			LoadReportingServerName: newString(testLRSServerName),
   197  			DropCategories: []DropConfig{{
   198  				Category:           dropReason2,
   199  				RequestsPerMillion: million * dropNumerator2 / dropDenominator2,
   200  			}},
   201  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   202  				Name: roundrobin.Name,
   203  			},
   204  		},
   205  	}); err != nil {
   206  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   207  	}
   208  
   209  	p2 := <-cc.NewPickerCh
   210  	for i := 0; i < rpcCount; i++ {
   211  		gotSCSt, err := p2.Pick(balancer.PickInfo{})
   212  		// Even RPCs are dropped.
   213  		if i%4 == 0 {
   214  			if err == nil || !strings.Contains(err.Error(), "dropped") {
   215  				t.Fatalf("pick.Pick, got %v, %v, want error RPC dropped", gotSCSt, err)
   216  			}
   217  			continue
   218  		}
   219  		if err != nil || !cmp.Equal(gotSCSt.SubConn, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) {
   220  			t.Fatalf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
   221  		}
   222  		if gotSCSt.Done != nil {
   223  			gotSCSt.Done(balancer.DoneInfo{})
   224  		}
   225  	}
   226  
   227  	const dropCount2 = rpcCount * dropNumerator2 / dropDenominator2
   228  	wantStatsData1 := []*load.Data{{
   229  		Cluster:    testClusterName,
   230  		Service:    testServiceName,
   231  		TotalDrops: dropCount2,
   232  		Drops:      map[string]uint64{dropReason2: dropCount2},
   233  		LocalityStats: map[string]load.LocalityData{
   234  			assertString(xdsinternal.LocalityID{}.ToString): {RequestStats: load.RequestData{Succeeded: rpcCount - dropCount2}},
   235  		},
   236  	}}
   237  
   238  	gotStatsData1 := loadStore.Stats([]string{testClusterName})
   239  	if diff := cmp.Diff(gotStatsData1, wantStatsData1, cmpOpts); diff != "" {
   240  		t.Fatalf("got unexpected reports, diff (-got, +want): %v", diff)
   241  	}
   242  }
   243  
   244  // TestDropCircuitBreaking verifies that the balancer correctly drops the picks
   245  // due to circuit breaking, and that the drops are reported.
   246  func (s) TestDropCircuitBreaking(t *testing.T) {
   247  	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
   248  	xdsC := fakeclient.NewClient()
   249  	defer xdsC.Close()
   250  
   251  	builder := balancer.Get(Name)
   252  	cc := testutils.NewTestClientConn(t)
   253  	b := builder.Build(cc, balancer.BuildOptions{})
   254  	defer b.Close()
   255  
   256  	var maxRequest uint32 = 50
   257  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   258  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   259  		BalancerConfig: &LBConfig{
   260  			Cluster:                 testClusterName,
   261  			EDSServiceName:          testServiceName,
   262  			LoadReportingServerName: newString(testLRSServerName),
   263  			MaxConcurrentRequests:   &maxRequest,
   264  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   265  				Name: roundrobin.Name,
   266  			},
   267  		},
   268  	}); err != nil {
   269  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   270  	}
   271  
   272  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   273  	defer cancel()
   274  
   275  	got, err := xdsC.WaitForReportLoad(ctx)
   276  	if err != nil {
   277  		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
   278  	}
   279  	if got.Server != testLRSServerName {
   280  		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerName)
   281  	}
   282  
   283  	sc1 := <-cc.NewSubConnCh
   284  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   285  	// This should get the connecting picker.
   286  	p0 := <-cc.NewPickerCh
   287  	for i := 0; i < 10; i++ {
   288  		_, err := p0.Pick(balancer.PickInfo{})
   289  		if err != balancer.ErrNoSubConnAvailable {
   290  			t.Fatalf("picker.Pick, got _,%v, want Err=%v", err, balancer.ErrNoSubConnAvailable)
   291  		}
   292  	}
   293  
   294  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   295  	// Test pick with one backend.
   296  	dones := []func(){}
   297  	p1 := <-cc.NewPickerCh
   298  	const rpcCount = 100
   299  	for i := 0; i < rpcCount; i++ {
   300  		gotSCSt, err := p1.Pick(balancer.PickInfo{})
   301  		if i < 50 && err != nil {
   302  			t.Errorf("The first 50%% picks should be non-drops, got error %v", err)
   303  		} else if i > 50 && err == nil {
   304  			t.Errorf("The second 50%% picks should be drops, got error <nil>")
   305  		}
   306  		dones = append(dones, func() {
   307  			if gotSCSt.Done != nil {
   308  				gotSCSt.Done(balancer.DoneInfo{})
   309  			}
   310  		})
   311  	}
   312  	for _, done := range dones {
   313  		done()
   314  	}
   315  
   316  	dones = []func(){}
   317  	// Pick without drops.
   318  	for i := 0; i < 50; i++ {
   319  		gotSCSt, err := p1.Pick(balancer.PickInfo{})
   320  		if err != nil {
   321  			t.Errorf("The third 50%% picks should be non-drops, got error %v", err)
   322  		}
   323  		dones = append(dones, func() {
   324  			if gotSCSt.Done != nil {
   325  				gotSCSt.Done(balancer.DoneInfo{})
   326  			}
   327  		})
   328  	}
   329  	for _, done := range dones {
   330  		done()
   331  	}
   332  
   333  	// Dump load data from the store and compare with expected counts.
   334  	loadStore := xdsC.LoadStore()
   335  	if loadStore == nil {
   336  		t.Fatal("loadStore is nil in xdsClient")
   337  	}
   338  
   339  	wantStatsData0 := []*load.Data{{
   340  		Cluster:    testClusterName,
   341  		Service:    testServiceName,
   342  		TotalDrops: uint64(maxRequest),
   343  		LocalityStats: map[string]load.LocalityData{
   344  			assertString(xdsinternal.LocalityID{}.ToString): {RequestStats: load.RequestData{Succeeded: uint64(rpcCount - maxRequest + 50)}},
   345  		},
   346  	}}
   347  
   348  	gotStatsData0 := loadStore.Stats([]string{testClusterName})
   349  	if diff := cmp.Diff(gotStatsData0, wantStatsData0, cmpOpts); diff != "" {
   350  		t.Fatalf("got unexpected drop reports, diff (-got, +want): %v", diff)
   351  	}
   352  }
   353  
   354  // TestPickerUpdateAfterClose covers the case where a child policy sends a
   355  // picker update after the cluster_impl policy is closed. Because picker updates
   356  // are handled in the run() goroutine, which exits before Close() returns, we
   357  // expect the above picker update to be dropped.
   358  func (s) TestPickerUpdateAfterClose(t *testing.T) {
   359  	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
   360  	xdsC := fakeclient.NewClient()
   361  	defer xdsC.Close()
   362  
   363  	builder := balancer.Get(Name)
   364  	cc := testutils.NewTestClientConn(t)
   365  	b := builder.Build(cc, balancer.BuildOptions{})
   366  
   367  	// Create a stub balancer which waits for the cluster_impl policy to be
   368  	// closed before sending a picker update (upon receipt of a subConn state
   369  	// change).
   370  	closeCh := make(chan struct{})
   371  	const childPolicyName = "stubBalancer-TestPickerUpdateAfterClose"
   372  	stub.Register(childPolicyName, stub.BalancerFuncs{
   373  		UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
   374  			// Create a subConn which will be used later on to test the race
   375  			// between UpdateSubConnState() and Close().
   376  			bd.ClientConn.NewSubConn(ccs.ResolverState.Addresses, balancer.NewSubConnOptions{})
   377  			return nil
   378  		},
   379  		UpdateSubConnState: func(bd *stub.BalancerData, _ balancer.SubConn, _ balancer.SubConnState) {
   380  			go func() {
   381  				// Wait for Close() to be called on the parent policy before
   382  				// sending the picker update.
   383  				<-closeCh
   384  				bd.ClientConn.UpdateState(balancer.State{
   385  					Picker: base.NewErrPicker(errors.New("dummy error picker")),
   386  				})
   387  			}()
   388  		},
   389  	})
   390  
   391  	var maxRequest uint32 = 50
   392  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   393  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   394  		BalancerConfig: &LBConfig{
   395  			Cluster:               testClusterName,
   396  			EDSServiceName:        testServiceName,
   397  			MaxConcurrentRequests: &maxRequest,
   398  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   399  				Name: childPolicyName,
   400  			},
   401  		},
   402  	}); err != nil {
   403  		b.Close()
   404  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   405  	}
   406  
   407  	// Send a subConn state change to trigger a picker update. The stub balancer
   408  	// that we use as the child policy will not send a picker update until the
   409  	// parent policy is closed.
   410  	sc1 := <-cc.NewSubConnCh
   411  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   412  	b.Close()
   413  	close(closeCh)
   414  
   415  	select {
   416  	case <-cc.NewPickerCh:
   417  		t.Fatalf("unexpected picker update after balancer is closed")
   418  	case <-time.After(defaultShortTestTimeout):
   419  	}
   420  }
   421  
   422  // TestClusterNameInAddressAttributes covers the case that cluster name is
   423  // attached to the subconn address attributes.
   424  func (s) TestClusterNameInAddressAttributes(t *testing.T) {
   425  	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
   426  	xdsC := fakeclient.NewClient()
   427  	defer xdsC.Close()
   428  
   429  	builder := balancer.Get(Name)
   430  	cc := testutils.NewTestClientConn(t)
   431  	b := builder.Build(cc, balancer.BuildOptions{})
   432  	defer b.Close()
   433  
   434  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   435  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   436  		BalancerConfig: &LBConfig{
   437  			Cluster:        testClusterName,
   438  			EDSServiceName: testServiceName,
   439  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   440  				Name: roundrobin.Name,
   441  			},
   442  		},
   443  	}); err != nil {
   444  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   445  	}
   446  
   447  	sc1 := <-cc.NewSubConnCh
   448  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   449  	// This should get the connecting picker.
   450  	p0 := <-cc.NewPickerCh
   451  	for i := 0; i < 10; i++ {
   452  		_, err := p0.Pick(balancer.PickInfo{})
   453  		if err != balancer.ErrNoSubConnAvailable {
   454  			t.Fatalf("picker.Pick, got _,%v, want Err=%v", err, balancer.ErrNoSubConnAvailable)
   455  		}
   456  	}
   457  
   458  	addrs1 := <-cc.NewSubConnAddrsCh
   459  	if got, want := addrs1[0].Addr, testBackendAddrs[0].Addr; got != want {
   460  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   461  	}
   462  	cn, ok := internal.GetXDSHandshakeClusterName(addrs1[0].Attributes)
   463  	if !ok || cn != testClusterName {
   464  		t.Fatalf("sc is created with addr with cluster name %v, %v, want cluster name %v", cn, ok, testClusterName)
   465  	}
   466  
   467  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   468  	// Test pick with one backend.
   469  	p1 := <-cc.NewPickerCh
   470  	const rpcCount = 20
   471  	for i := 0; i < rpcCount; i++ {
   472  		gotSCSt, err := p1.Pick(balancer.PickInfo{})
   473  		if err != nil || !cmp.Equal(gotSCSt.SubConn, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) {
   474  			t.Fatalf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
   475  		}
   476  		if gotSCSt.Done != nil {
   477  			gotSCSt.Done(balancer.DoneInfo{})
   478  		}
   479  	}
   480  
   481  	const testClusterName2 = "test-cluster-2"
   482  	var addr2 = resolver.Address{Addr: "2.2.2.2"}
   483  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   484  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: []resolver.Address{addr2}}, xdsC),
   485  		BalancerConfig: &LBConfig{
   486  			Cluster:        testClusterName2,
   487  			EDSServiceName: testServiceName,
   488  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   489  				Name: roundrobin.Name,
   490  			},
   491  		},
   492  	}); err != nil {
   493  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   494  	}
   495  
   496  	addrs2 := <-cc.NewSubConnAddrsCh
   497  	if got, want := addrs2[0].Addr, addr2.Addr; got != want {
   498  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   499  	}
   500  	// New addresses should have the new cluster name.
   501  	cn2, ok := internal.GetXDSHandshakeClusterName(addrs2[0].Attributes)
   502  	if !ok || cn2 != testClusterName2 {
   503  		t.Fatalf("sc is created with addr with cluster name %v, %v, want cluster name %v", cn2, ok, testClusterName2)
   504  	}
   505  }
   506  
   507  // TestReResolution verifies that when a SubConn turns transient failure,
   508  // re-resolution is triggered.
   509  func (s) TestReResolution(t *testing.T) {
   510  	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
   511  	xdsC := fakeclient.NewClient()
   512  	defer xdsC.Close()
   513  
   514  	builder := balancer.Get(Name)
   515  	cc := testutils.NewTestClientConn(t)
   516  	b := builder.Build(cc, balancer.BuildOptions{})
   517  	defer b.Close()
   518  
   519  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   520  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC),
   521  		BalancerConfig: &LBConfig{
   522  			Cluster:        testClusterName,
   523  			EDSServiceName: testServiceName,
   524  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   525  				Name: roundrobin.Name,
   526  			},
   527  		},
   528  	}); err != nil {
   529  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   530  	}
   531  
   532  	sc1 := <-cc.NewSubConnCh
   533  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   534  	// This should get the connecting picker.
   535  	p0 := <-cc.NewPickerCh
   536  	for i := 0; i < 10; i++ {
   537  		_, err := p0.Pick(balancer.PickInfo{})
   538  		if err != balancer.ErrNoSubConnAvailable {
   539  			t.Fatalf("picker.Pick, got _,%v, want Err=%v", err, balancer.ErrNoSubConnAvailable)
   540  		}
   541  	}
   542  
   543  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   544  	// This should get the transient failure picker.
   545  	p1 := <-cc.NewPickerCh
   546  	for i := 0; i < 10; i++ {
   547  		_, err := p1.Pick(balancer.PickInfo{})
   548  		if err == nil {
   549  			t.Fatalf("picker.Pick, got _,%v, want not nil", err)
   550  		}
   551  	}
   552  
   553  	// The transient failure should trigger a re-resolution.
   554  	select {
   555  	case <-cc.ResolveNowCh:
   556  	case <-time.After(defaultTestTimeout):
   557  		t.Fatalf("timeout waiting for ResolveNow()")
   558  	}
   559  
   560  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   561  	// Test pick with one backend.
   562  	p2 := <-cc.NewPickerCh
   563  	want := []balancer.SubConn{sc1}
   564  	if err := testutils.IsRoundRobin(want, subConnFromPicker(p2)); err != nil {
   565  		t.Fatalf("want %v, got %v", want, err)
   566  	}
   567  
   568  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   569  	// This should get the transient failure picker.
   570  	p3 := <-cc.NewPickerCh
   571  	for i := 0; i < 10; i++ {
   572  		_, err := p3.Pick(balancer.PickInfo{})
   573  		if err == nil {
   574  			t.Fatalf("picker.Pick, got _,%v, want not nil", err)
   575  		}
   576  	}
   577  
   578  	// The transient failure should trigger a re-resolution.
   579  	select {
   580  	case <-cc.ResolveNowCh:
   581  	case <-time.After(defaultTestTimeout):
   582  		t.Fatalf("timeout waiting for ResolveNow()")
   583  	}
   584  }
   585  
   586  func (s) TestLoadReporting(t *testing.T) {
   587  	var testLocality = xdsinternal.LocalityID{
   588  		Region:  "test-region",
   589  		Zone:    "test-zone",
   590  		SubZone: "test-sub-zone",
   591  	}
   592  
   593  	xdsC := fakeclient.NewClient()
   594  	defer xdsC.Close()
   595  
   596  	builder := balancer.Get(Name)
   597  	cc := testutils.NewTestClientConn(t)
   598  	b := builder.Build(cc, balancer.BuildOptions{})
   599  	defer b.Close()
   600  
   601  	addrs := make([]resolver.Address, len(testBackendAddrs))
   602  	for i, a := range testBackendAddrs {
   603  		addrs[i] = xdsinternal.SetLocalityID(a, testLocality)
   604  	}
   605  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   606  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC),
   607  		BalancerConfig: &LBConfig{
   608  			Cluster:                 testClusterName,
   609  			EDSServiceName:          testServiceName,
   610  			LoadReportingServerName: newString(testLRSServerName),
   611  			// Locality:                testLocality,
   612  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   613  				Name: roundrobin.Name,
   614  			},
   615  		},
   616  	}); err != nil {
   617  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   618  	}
   619  
   620  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   621  	defer cancel()
   622  
   623  	got, err := xdsC.WaitForReportLoad(ctx)
   624  	if err != nil {
   625  		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
   626  	}
   627  	if got.Server != testLRSServerName {
   628  		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerName)
   629  	}
   630  
   631  	sc1 := <-cc.NewSubConnCh
   632  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   633  	// This should get the connecting picker.
   634  	p0 := <-cc.NewPickerCh
   635  	for i := 0; i < 10; i++ {
   636  		_, err := p0.Pick(balancer.PickInfo{})
   637  		if err != balancer.ErrNoSubConnAvailable {
   638  			t.Fatalf("picker.Pick, got _,%v, want Err=%v", err, balancer.ErrNoSubConnAvailable)
   639  		}
   640  	}
   641  
   642  	b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   643  	// Test pick with one backend.
   644  	p1 := <-cc.NewPickerCh
   645  	const successCount = 5
   646  	for i := 0; i < successCount; i++ {
   647  		gotSCSt, err := p1.Pick(balancer.PickInfo{})
   648  		if !cmp.Equal(gotSCSt.SubConn, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) {
   649  			t.Fatalf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
   650  		}
   651  		gotSCSt.Done(balancer.DoneInfo{})
   652  	}
   653  	const errorCount = 5
   654  	for i := 0; i < errorCount; i++ {
   655  		gotSCSt, err := p1.Pick(balancer.PickInfo{})
   656  		if !cmp.Equal(gotSCSt.SubConn, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) {
   657  			t.Fatalf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
   658  		}
   659  		gotSCSt.Done(balancer.DoneInfo{Err: fmt.Errorf("error")})
   660  	}
   661  
   662  	// Dump load data from the store and compare with expected counts.
   663  	loadStore := xdsC.LoadStore()
   664  	if loadStore == nil {
   665  		t.Fatal("loadStore is nil in xdsClient")
   666  	}
   667  	sds := loadStore.Stats([]string{testClusterName})
   668  	if len(sds) == 0 {
   669  		t.Fatalf("loads for cluster %v not found in store", testClusterName)
   670  	}
   671  	sd := sds[0]
   672  	if sd.Cluster != testClusterName || sd.Service != testServiceName {
   673  		t.Fatalf("got unexpected load for %q, %q, want %q, %q", sd.Cluster, sd.Service, testClusterName, testServiceName)
   674  	}
   675  	testLocalityJSON, _ := testLocality.ToString()
   676  	localityData, ok := sd.LocalityStats[testLocalityJSON]
   677  	if !ok {
   678  		t.Fatalf("loads for %v not found in store", testLocality)
   679  	}
   680  	reqStats := localityData.RequestStats
   681  	if reqStats.Succeeded != successCount {
   682  		t.Errorf("got succeeded %v, want %v", reqStats.Succeeded, successCount)
   683  	}
   684  	if reqStats.Errored != errorCount {
   685  		t.Errorf("got errord %v, want %v", reqStats.Errored, errorCount)
   686  	}
   687  	if reqStats.InProgress != 0 {
   688  		t.Errorf("got inProgress %v, want %v", reqStats.InProgress, 0)
   689  	}
   690  
   691  	b.Close()
   692  	if err := xdsC.WaitForCancelReportLoad(ctx); err != nil {
   693  		t.Fatalf("unexpected error waiting form load report to be canceled: %v", err)
   694  	}
   695  }
   696  
   697  // TestUpdateLRSServer covers the cases
   698  // - the init config specifies "" as the LRS server
   699  // - config modifies LRS server to a different string
   700  // - config sets LRS server to nil to stop load reporting
   701  func (s) TestUpdateLRSServer(t *testing.T) {
   702  	var testLocality = xdsinternal.LocalityID{
   703  		Region:  "test-region",
   704  		Zone:    "test-zone",
   705  		SubZone: "test-sub-zone",
   706  	}
   707  
   708  	xdsC := fakeclient.NewClient()
   709  	defer xdsC.Close()
   710  
   711  	builder := balancer.Get(Name)
   712  	cc := testutils.NewTestClientConn(t)
   713  	b := builder.Build(cc, balancer.BuildOptions{})
   714  	defer b.Close()
   715  
   716  	addrs := make([]resolver.Address, len(testBackendAddrs))
   717  	for i, a := range testBackendAddrs {
   718  		addrs[i] = xdsinternal.SetLocalityID(a, testLocality)
   719  	}
   720  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   721  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC),
   722  		BalancerConfig: &LBConfig{
   723  			Cluster:                 testClusterName,
   724  			EDSServiceName:          testServiceName,
   725  			LoadReportingServerName: newString(""),
   726  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   727  				Name: roundrobin.Name,
   728  			},
   729  		},
   730  	}); err != nil {
   731  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   732  	}
   733  
   734  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   735  	defer cancel()
   736  
   737  	got, err := xdsC.WaitForReportLoad(ctx)
   738  	if err != nil {
   739  		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
   740  	}
   741  	if got.Server != "" {
   742  		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, "")
   743  	}
   744  
   745  	// Update LRS server to a different name.
   746  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   747  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC),
   748  		BalancerConfig: &LBConfig{
   749  			Cluster:                 testClusterName,
   750  			EDSServiceName:          testServiceName,
   751  			LoadReportingServerName: newString(testLRSServerName),
   752  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   753  				Name: roundrobin.Name,
   754  			},
   755  		},
   756  	}); err != nil {
   757  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   758  	}
   759  	if err := xdsC.WaitForCancelReportLoad(ctx); err != nil {
   760  		t.Fatalf("unexpected error waiting form load report to be canceled: %v", err)
   761  	}
   762  	got2, err2 := xdsC.WaitForReportLoad(ctx)
   763  	if err2 != nil {
   764  		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err2)
   765  	}
   766  	if got2.Server != testLRSServerName {
   767  		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got2.Server, testLRSServerName)
   768  	}
   769  
   770  	// Update LRS server to nil, to disable LRS.
   771  	if err := b.UpdateClientConnState(balancer.ClientConnState{
   772  		ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC),
   773  		BalancerConfig: &LBConfig{
   774  			Cluster:                 testClusterName,
   775  			EDSServiceName:          testServiceName,
   776  			LoadReportingServerName: nil,
   777  			ChildPolicy: &internalserviceconfig.BalancerConfig{
   778  				Name: roundrobin.Name,
   779  			},
   780  		},
   781  	}); err != nil {
   782  		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
   783  	}
   784  	if err := xdsC.WaitForCancelReportLoad(ctx); err != nil {
   785  		t.Fatalf("unexpected error waiting form load report to be canceled: %v", err)
   786  	}
   787  
   788  	shortCtx, shortCancel := context.WithTimeout(context.Background(), defaultShortTestTimeout)
   789  	defer shortCancel()
   790  	if s, err := xdsC.WaitForReportLoad(shortCtx); err != context.DeadlineExceeded {
   791  		t.Fatalf("unexpected load report to server: %q", s)
   792  	}
   793  }
   794  
   795  func assertString(f func() (string, error)) string {
   796  	s, err := f()
   797  	if err != nil {
   798  		panic(err.Error())
   799  	}
   800  	return s
   801  }