gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/xds/internal/balancer/clusterresolver/priority_test.go (about)

     1  /*
     2   *
     3   * Copyright 2019 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 clusterresolver
    19  
    20  import (
    21  	"context"
    22  	"net/url"
    23  	"testing"
    24  	"time"
    25  
    26  	corepb "gitee.com/ks-custle/core-gm/go-control-plane/envoy/api/v2/core"
    27  	"gitee.com/ks-custle/core-gm/grpc/balancer"
    28  	"gitee.com/ks-custle/core-gm/grpc/connectivity"
    29  	"gitee.com/ks-custle/core-gm/grpc/internal/testutils"
    30  	"gitee.com/ks-custle/core-gm/grpc/resolver"
    31  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/balancer/priority"
    32  	xdstestutils "gitee.com/ks-custle/core-gm/grpc/xds/internal/testutils"
    33  	"github.com/google/go-cmp/cmp"
    34  )
    35  
    36  // When a high priority is ready, adding/removing lower locality doesn't cause
    37  // changes.
    38  //
    39  // Init 0 and 1; 0 is up, use 0; add 2, use 0; remove 2, use 0.
    40  func (s) TestEDSPriority_HighPriorityReady(t *testing.T) {
    41  	edsb, cc, xdsC, cleanup := setupTestEDS(t, nil)
    42  	defer cleanup()
    43  
    44  	// Two localities, with priorities [0, 1], each with one backend.
    45  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
    46  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
    47  	clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
    48  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
    49  
    50  	addrs1 := <-cc.NewSubConnAddrsCh
    51  	if got, want := addrs1[0].Addr, testEndpointAddrs[0]; got != want {
    52  		t.Fatalf("sc is created with addr %v, want %v", got, want)
    53  	}
    54  	sc1 := <-cc.NewSubConnCh
    55  
    56  	// p0 is ready.
    57  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
    58  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
    59  
    60  	// Test roundrobin with only p0 subconns.
    61  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil {
    62  		t.Fatal(err)
    63  	}
    64  
    65  	// Add p2, it shouldn't cause any updates.
    66  	clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
    67  	clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
    68  	clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
    69  	clab2.AddLocality(testSubZones[2], 1, 2, testEndpointAddrs[2:3], nil)
    70  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil)
    71  
    72  	select {
    73  	case <-cc.NewPickerCh:
    74  		t.Fatalf("got unexpected new picker")
    75  	case <-cc.NewSubConnCh:
    76  		t.Fatalf("got unexpected new SubConn")
    77  	case <-cc.RemoveSubConnCh:
    78  		t.Fatalf("got unexpected remove SubConn")
    79  	case <-time.After(defaultTestShortTimeout):
    80  	}
    81  
    82  	// Remove p2, no updates.
    83  	clab3 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
    84  	clab3.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
    85  	clab3.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
    86  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab3.Build()), nil)
    87  
    88  	select {
    89  	case <-cc.NewPickerCh:
    90  		t.Fatalf("got unexpected new picker")
    91  	case <-cc.NewSubConnCh:
    92  		t.Fatalf("got unexpected new SubConn")
    93  	case <-cc.RemoveSubConnCh:
    94  		t.Fatalf("got unexpected remove SubConn")
    95  	case <-time.After(defaultTestShortTimeout):
    96  	}
    97  }
    98  
    99  // Lower priority is used when higher priority is not ready.
   100  //
   101  // Init 0 and 1; 0 is up, use 0; 0 is down, 1 is up, use 1; add 2, use 1; 1 is
   102  // down, use 2; remove 2, use 1.
   103  func (s) TestEDSPriority_SwitchPriority(t *testing.T) {
   104  	edsb, cc, xdsC, cleanup := setupTestEDS(t, nil)
   105  	defer cleanup()
   106  
   107  	// Two localities, with priorities [0, 1], each with one backend.
   108  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   109  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   110  	clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   111  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
   112  
   113  	addrs0 := <-cc.NewSubConnAddrsCh
   114  	if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want {
   115  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   116  	}
   117  	sc0 := <-cc.NewSubConnCh
   118  
   119  	// p0 is ready.
   120  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   121  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   122  
   123  	// Test roundrobin with only p0 subconns.
   124  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil {
   125  		t.Fatal(err)
   126  	}
   127  
   128  	// Turn down 0, 1 is used.
   129  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   130  	addrs1 := <-cc.NewSubConnAddrsCh
   131  	if got, want := addrs1[0].Addr, testEndpointAddrs[1]; got != want {
   132  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   133  	}
   134  	sc1 := <-cc.NewSubConnCh
   135  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   136  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   137  
   138  	// Test pick with 1.
   139  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil {
   140  		t.Fatal(err)
   141  	}
   142  
   143  	// Add p2, it shouldn't cause any updates.
   144  	clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   145  	clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   146  	clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   147  	clab2.AddLocality(testSubZones[2], 1, 2, testEndpointAddrs[2:3], nil)
   148  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil)
   149  
   150  	select {
   151  	case <-cc.NewPickerCh:
   152  		t.Fatalf("got unexpected new picker")
   153  	case <-cc.NewSubConnCh:
   154  		t.Fatalf("got unexpected new SubConn")
   155  	case <-cc.RemoveSubConnCh:
   156  		t.Fatalf("got unexpected remove SubConn")
   157  	case <-time.After(defaultTestShortTimeout):
   158  	}
   159  
   160  	// Turn down 1, use 2
   161  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   162  	addrs2 := <-cc.NewSubConnAddrsCh
   163  	if got, want := addrs2[0].Addr, testEndpointAddrs[2]; got != want {
   164  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   165  	}
   166  	sc2 := <-cc.NewSubConnCh
   167  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   168  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   169  
   170  	// Test pick with 2.
   171  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil {
   172  		t.Fatal(err)
   173  	}
   174  
   175  	// Remove 2, use 1.
   176  	clab3 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   177  	clab3.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   178  	clab3.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   179  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab3.Build()), nil)
   180  
   181  	// p2 SubConns are removed.
   182  	scToRemove := <-cc.RemoveSubConnCh
   183  	if !cmp.Equal(scToRemove, sc2, cmp.AllowUnexported(testutils.TestSubConn{})) {
   184  		t.Fatalf("RemoveSubConn, want %v, got %v", sc2, scToRemove)
   185  	}
   186  
   187  	// Should get an update with 1's old picker, to override 2's old picker.
   188  	if err := testErrPickerFromCh(cc.NewPickerCh, balancer.ErrTransientFailure); err != nil {
   189  		t.Fatal(err)
   190  	}
   191  
   192  }
   193  
   194  // Add a lower priority while the higher priority is down.
   195  //
   196  // Init 0 and 1; 0 and 1 both down; add 2, use 2.
   197  func (s) TestEDSPriority_HigherDownWhileAddingLower(t *testing.T) {
   198  	edsb, cc, xdsC, cleanup := setupTestEDS(t, nil)
   199  	defer cleanup()
   200  	// Two localities, with different priorities, each with one backend.
   201  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   202  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   203  	clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   204  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
   205  	addrs0 := <-cc.NewSubConnAddrsCh
   206  	if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want {
   207  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   208  	}
   209  	sc0 := <-cc.NewSubConnCh
   210  
   211  	// Turn down 0, 1 is used.
   212  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   213  	addrs1 := <-cc.NewSubConnAddrsCh
   214  	if got, want := addrs1[0].Addr, testEndpointAddrs[1]; got != want {
   215  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   216  	}
   217  	sc1 := <-cc.NewSubConnCh
   218  	// Turn down 1, pick should error.
   219  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   220  
   221  	// Test pick failure.
   222  	if err := testErrPickerFromCh(cc.NewPickerCh, balancer.ErrTransientFailure); err != nil {
   223  		t.Fatal(err)
   224  	}
   225  
   226  	// Add p2, it should create a new SubConn.
   227  	clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   228  	clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   229  	clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   230  	clab2.AddLocality(testSubZones[2], 1, 2, testEndpointAddrs[2:3], nil)
   231  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil)
   232  	addrs2 := <-cc.NewSubConnAddrsCh
   233  	if got, want := addrs2[0].Addr, testEndpointAddrs[2]; got != want {
   234  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   235  	}
   236  	sc2 := <-cc.NewSubConnCh
   237  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   238  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   239  
   240  	// Test pick with 2.
   241  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil {
   242  		t.Fatal(err)
   243  	}
   244  
   245  }
   246  
   247  // When a higher priority becomes available, all lower priorities are closed.
   248  //
   249  // Init 0,1,2; 0 and 1 down, use 2; 0 up, close 1 and 2.
   250  func (s) TestEDSPriority_HigherReadyCloseAllLower(t *testing.T) {
   251  	edsb, cc, xdsC, cleanup := setupTestEDS(t, nil)
   252  	defer cleanup()
   253  	// Two localities, with priorities [0,1,2], each with one backend.
   254  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   255  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   256  	clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   257  	clab1.AddLocality(testSubZones[2], 1, 2, testEndpointAddrs[2:3], nil)
   258  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
   259  	addrs0 := <-cc.NewSubConnAddrsCh
   260  	if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want {
   261  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   262  	}
   263  	sc0 := <-cc.NewSubConnCh
   264  
   265  	// Turn down 0, 1 is used.
   266  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   267  	addrs1 := <-cc.NewSubConnAddrsCh
   268  	if got, want := addrs1[0].Addr, testEndpointAddrs[1]; got != want {
   269  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   270  	}
   271  	sc1 := <-cc.NewSubConnCh
   272  	// Turn down 1, 2 is used.
   273  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   274  	addrs2 := <-cc.NewSubConnAddrsCh
   275  	if got, want := addrs2[0].Addr, testEndpointAddrs[2]; got != want {
   276  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   277  	}
   278  	sc2 := <-cc.NewSubConnCh
   279  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   280  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   281  
   282  	// Test pick with 2.
   283  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil {
   284  		t.Fatal(err)
   285  	}
   286  
   287  	// When 0 becomes ready, 0 should be used, 1 and 2 should all be closed.
   288  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   289  	var (
   290  		scToRemove    []balancer.SubConn
   291  		scToRemoveMap = make(map[balancer.SubConn]struct{})
   292  	)
   293  	// Each subconn is removed twice. This is OK in production, but it makes
   294  	// testing harder.
   295  	//
   296  	// The sub-balancer to be closed is priority's child, clusterimpl, who has
   297  	// weightedtarget as children.
   298  	//
   299  	// - When clusterimpl is removed from priority's balancergroup, all its
   300  	// subconns are removed once.
   301  	// - When clusterimpl is closed, it closes weightedtarget, and this
   302  	// weightedtarget's balancer removes all the same subconns again.
   303  	for i := 0; i < 4; i++ {
   304  		// We expect 2 subconns, so we recv from channel 4 times.
   305  		scToRemoveMap[<-cc.RemoveSubConnCh] = struct{}{}
   306  	}
   307  	for sc := range scToRemoveMap {
   308  		scToRemove = append(scToRemove, sc)
   309  	}
   310  
   311  	// sc1 and sc2 should be removed.
   312  	//
   313  	// With localities caching, the lower priorities are closed after a timeout,
   314  	// in goroutines. The order is no longer guaranteed.
   315  	if !(cmp.Equal(scToRemove[0], sc1, cmp.AllowUnexported(testutils.TestSubConn{})) &&
   316  		cmp.Equal(scToRemove[1], sc2, cmp.AllowUnexported(testutils.TestSubConn{}))) &&
   317  		!(cmp.Equal(scToRemove[0], sc2, cmp.AllowUnexported(testutils.TestSubConn{})) &&
   318  			cmp.Equal(scToRemove[1], sc1, cmp.AllowUnexported(testutils.TestSubConn{}))) {
   319  		t.Errorf("RemoveSubConn, want [%v, %v], got %v", sc1, sc2, scToRemove)
   320  	}
   321  
   322  	// Test pick with 0.
   323  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil {
   324  		t.Fatal(err)
   325  	}
   326  }
   327  
   328  // At init, start the next lower priority after timeout if the higher priority
   329  // doesn't get ready.
   330  //
   331  // Init 0,1; 0 is not ready (in connecting), after timeout, use 1.
   332  func (s) TestEDSPriority_InitTimeout(t *testing.T) {
   333  	const testPriorityInitTimeout = time.Second
   334  	defer func() func() {
   335  		old := priority.DefaultPriorityInitTimeout
   336  		priority.DefaultPriorityInitTimeout = testPriorityInitTimeout
   337  		return func() {
   338  			priority.DefaultPriorityInitTimeout = old
   339  		}
   340  	}()()
   341  
   342  	edsb, cc, xdsC, cleanup := setupTestEDS(t, nil)
   343  	defer cleanup()
   344  	// Two localities, with different priorities, each with one backend.
   345  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   346  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   347  	clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   348  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
   349  	addrs0 := <-cc.NewSubConnAddrsCh
   350  	if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want {
   351  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   352  	}
   353  	sc0 := <-cc.NewSubConnCh
   354  
   355  	// Keep 0 in connecting, 1 will be used after init timeout.
   356  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   357  
   358  	// Make sure new SubConn is created before timeout.
   359  	select {
   360  	case <-time.After(testPriorityInitTimeout * 3 / 4):
   361  	case <-cc.NewSubConnAddrsCh:
   362  		t.Fatalf("Got a new SubConn too early (Within timeout). Expect a new SubConn only after timeout")
   363  	}
   364  
   365  	addrs1 := <-cc.NewSubConnAddrsCh
   366  	if got, want := addrs1[0].Addr, testEndpointAddrs[1]; got != want {
   367  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   368  	}
   369  	sc1 := <-cc.NewSubConnCh
   370  
   371  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   372  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   373  
   374  	// Test pick with 1.
   375  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil {
   376  		t.Fatal(err)
   377  	}
   378  }
   379  
   380  // Add localities to existing priorities.
   381  //
   382  //   - start with 2 locality with p0 and p1
   383  //   - add localities to existing p0 and p1
   384  func (s) TestEDSPriority_MultipleLocalities(t *testing.T) {
   385  	edsb, cc, xdsC, cleanup := setupTestEDS(t, nil)
   386  	defer cleanup()
   387  	// Two localities, with different priorities, each with one backend.
   388  	clab0 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   389  	clab0.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   390  	clab0.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   391  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab0.Build()), nil)
   392  	addrs0 := <-cc.NewSubConnAddrsCh
   393  	if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want {
   394  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   395  	}
   396  	sc0 := <-cc.NewSubConnCh
   397  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   398  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   399  
   400  	// Test roundrobin with only p0 subconns.
   401  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil {
   402  		t.Fatal(err)
   403  	}
   404  
   405  	// Turn down p0 subconns, p1 subconns will be created.
   406  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   407  
   408  	addrs1 := <-cc.NewSubConnAddrsCh
   409  	if got, want := addrs1[0].Addr, testEndpointAddrs[1]; got != want {
   410  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   411  	}
   412  	sc1 := <-cc.NewSubConnCh
   413  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   414  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   415  
   416  	// Test roundrobin with only p1 subconns.
   417  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil {
   418  		t.Fatal(err)
   419  	}
   420  
   421  	// Reconnect p0 subconns, p1 subconn will be closed.
   422  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   423  
   424  	scToRemove := <-cc.RemoveSubConnCh
   425  	if !cmp.Equal(scToRemove, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) {
   426  		t.Fatalf("RemoveSubConn, want %v, got %v", sc1, scToRemove)
   427  	}
   428  
   429  	// Test roundrobin with only p0 subconns.
   430  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil {
   431  		t.Fatal(err)
   432  	}
   433  
   434  	// Add two localities, with two priorities, with one backend.
   435  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   436  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   437  	clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   438  	clab1.AddLocality(testSubZones[2], 1, 0, testEndpointAddrs[2:3], nil)
   439  	clab1.AddLocality(testSubZones[3], 1, 1, testEndpointAddrs[3:4], nil)
   440  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
   441  	addrs2 := <-cc.NewSubConnAddrsCh
   442  	if got, want := addrs2[0].Addr, testEndpointAddrs[2]; got != want {
   443  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   444  	}
   445  	sc2 := <-cc.NewSubConnCh
   446  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   447  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   448  
   449  	// Test roundrobin with only two p0 subconns.
   450  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0, sc2}); err != nil {
   451  		t.Fatal(err)
   452  	}
   453  
   454  	// Turn down p0 subconns, p1 subconns will be created.
   455  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   456  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   457  
   458  	sc3 := <-cc.NewSubConnCh
   459  	edsb.UpdateSubConnState(sc3, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   460  	edsb.UpdateSubConnState(sc3, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   461  	sc4 := <-cc.NewSubConnCh
   462  	edsb.UpdateSubConnState(sc4, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   463  	edsb.UpdateSubConnState(sc4, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   464  
   465  	// Test roundrobin with only p1 subconns.
   466  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc3, sc4}); err != nil {
   467  		t.Fatal(err)
   468  	}
   469  }
   470  
   471  // EDS removes all localities, and re-adds them.
   472  func (s) TestEDSPriority_RemovesAllLocalities(t *testing.T) {
   473  	const testPriorityInitTimeout = time.Second
   474  	defer func() func() {
   475  		old := priority.DefaultPriorityInitTimeout
   476  		priority.DefaultPriorityInitTimeout = testPriorityInitTimeout
   477  		return func() {
   478  			priority.DefaultPriorityInitTimeout = old
   479  		}
   480  	}()()
   481  
   482  	edsb, cc, xdsC, cleanup := setupTestEDS(t, nil)
   483  	defer cleanup()
   484  	// Two localities, with different priorities, each with one backend.
   485  	clab0 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   486  	clab0.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   487  	clab0.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   488  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab0.Build()), nil)
   489  	addrs0 := <-cc.NewSubConnAddrsCh
   490  	if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want {
   491  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   492  	}
   493  	sc0 := <-cc.NewSubConnCh
   494  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   495  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   496  
   497  	// Test roundrobin with only p0 subconns.
   498  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil {
   499  		t.Fatal(err)
   500  	}
   501  
   502  	// Remove all priorities.
   503  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   504  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
   505  	// p0 subconn should be removed.
   506  	scToRemove := <-cc.RemoveSubConnCh
   507  	<-cc.RemoveSubConnCh // Drain the duplicate subconn removed.
   508  	if !cmp.Equal(scToRemove, sc0, cmp.AllowUnexported(testutils.TestSubConn{})) {
   509  		t.Fatalf("RemoveSubConn, want %v, got %v", sc0, scToRemove)
   510  	}
   511  
   512  	// time.Sleep(time.Second)
   513  
   514  	// Test pick return TransientFailure.
   515  	if err := testErrPickerFromCh(cc.NewPickerCh, priority.ErrAllPrioritiesRemoved); err != nil {
   516  		t.Fatal(err)
   517  	}
   518  
   519  	// Re-add two localities, with previous priorities, but different backends.
   520  	clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   521  	clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[2:3], nil)
   522  	clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[3:4], nil)
   523  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil)
   524  	addrs01 := <-cc.NewSubConnAddrsCh
   525  	if got, want := addrs01[0].Addr, testEndpointAddrs[2]; got != want {
   526  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   527  	}
   528  	sc01 := <-cc.NewSubConnCh
   529  
   530  	// Don't send any update to p0, so to not override the old state of p0.
   531  	// Later, connect to p1 and then remove p1. This will fallback to p0, and
   532  	// will send p0's old picker if they are not correctly removed.
   533  
   534  	// p1 will be used after priority init timeout.
   535  	addrs11 := <-cc.NewSubConnAddrsCh
   536  	if got, want := addrs11[0].Addr, testEndpointAddrs[3]; got != want {
   537  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   538  	}
   539  	sc11 := <-cc.NewSubConnCh
   540  	edsb.UpdateSubConnState(sc11, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   541  	edsb.UpdateSubConnState(sc11, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   542  
   543  	// Test roundrobin with only p1 subconns.
   544  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc11}); err != nil {
   545  		t.Fatal(err)
   546  	}
   547  
   548  	// Remove p1 from EDS, to fallback to p0.
   549  	clab3 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   550  	clab3.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[2:3], nil)
   551  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab3.Build()), nil)
   552  
   553  	// p1 subconn should be removed.
   554  	scToRemove1 := <-cc.RemoveSubConnCh
   555  	<-cc.RemoveSubConnCh // Drain the duplicate subconn removed.
   556  	if !cmp.Equal(scToRemove1, sc11, cmp.AllowUnexported(testutils.TestSubConn{})) {
   557  		t.Fatalf("RemoveSubConn, want %v, got %v", sc11, scToRemove1)
   558  	}
   559  
   560  	// Test pick return TransientFailure.
   561  	if err := testErrPickerFromCh(cc.NewPickerCh, balancer.ErrNoSubConnAvailable); err != nil {
   562  		t.Fatal(err)
   563  	}
   564  
   565  	// Send an ready update for the p0 sc that was received when re-adding
   566  	// localities to EDS.
   567  	edsb.UpdateSubConnState(sc01, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   568  	edsb.UpdateSubConnState(sc01, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   569  
   570  	// Test roundrobin with only p0 subconns.
   571  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc01}); err != nil {
   572  		t.Fatal(err)
   573  	}
   574  
   575  	select {
   576  	case <-cc.NewPickerCh:
   577  		t.Fatalf("got unexpected new picker")
   578  	case <-cc.NewSubConnCh:
   579  		t.Fatalf("got unexpected new SubConn")
   580  	case <-cc.RemoveSubConnCh:
   581  		t.Fatalf("got unexpected remove SubConn")
   582  	case <-time.After(defaultTestShortTimeout):
   583  	}
   584  }
   585  
   586  // Test the case where the high priority contains no backends. The low priority
   587  // will be used.
   588  func (s) TestEDSPriority_HighPriorityNoEndpoints(t *testing.T) {
   589  	edsb, cc, xdsC, cleanup := setupTestEDS(t, nil)
   590  	defer cleanup()
   591  	// Two localities, with priorities [0, 1], each with one backend.
   592  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   593  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   594  	clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   595  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
   596  	addrs1 := <-cc.NewSubConnAddrsCh
   597  	if got, want := addrs1[0].Addr, testEndpointAddrs[0]; got != want {
   598  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   599  	}
   600  	sc1 := <-cc.NewSubConnCh
   601  
   602  	// p0 is ready.
   603  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   604  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   605  
   606  	// Test roundrobin with only p0 subconns.
   607  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil {
   608  		t.Fatal(err)
   609  	}
   610  
   611  	// Remove addresses from priority 0, should use p1.
   612  	clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   613  	clab2.AddLocality(testSubZones[0], 1, 0, nil, nil)
   614  	clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   615  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil)
   616  	// p0 will remove the subconn, and ClientConn will send a sc update to
   617  	// shutdown.
   618  	scToRemove := <-cc.RemoveSubConnCh
   619  	edsb.UpdateSubConnState(scToRemove, balancer.SubConnState{ConnectivityState: connectivity.Shutdown})
   620  
   621  	addrs2 := <-cc.NewSubConnAddrsCh
   622  	if got, want := addrs2[0].Addr, testEndpointAddrs[1]; got != want {
   623  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   624  	}
   625  	sc2 := <-cc.NewSubConnCh
   626  
   627  	// p1 is ready.
   628  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   629  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   630  
   631  	// Test roundrobin with only p1 subconns.
   632  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil {
   633  		t.Fatal(err)
   634  	}
   635  }
   636  
   637  // Test the case where the high priority contains no healthy backends. The low
   638  // priority will be used.
   639  func (s) TestEDSPriority_HighPriorityAllUnhealthy(t *testing.T) {
   640  	edsb, cc, xdsC, cleanup := setupTestEDS(t, nil)
   641  	defer cleanup()
   642  	// Two localities, with priorities [0, 1], each with one backend.
   643  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   644  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   645  	clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   646  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
   647  	addrs1 := <-cc.NewSubConnAddrsCh
   648  	if got, want := addrs1[0].Addr, testEndpointAddrs[0]; got != want {
   649  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   650  	}
   651  	sc1 := <-cc.NewSubConnCh
   652  
   653  	// p0 is ready.
   654  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   655  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   656  
   657  	// Test roundrobin with only p0 subconns.
   658  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil {
   659  		t.Fatal(err)
   660  	}
   661  
   662  	// Set priority 0 endpoints to all unhealthy, should use p1.
   663  	clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   664  	clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], &xdstestutils.AddLocalityOptions{
   665  		Health: []corepb.HealthStatus{corepb.HealthStatus_UNHEALTHY},
   666  	})
   667  	clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil)
   668  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil)
   669  	// p0 will remove the subconn, and ClientConn will send a sc update to
   670  	// transient failure.
   671  	scToRemove := <-cc.RemoveSubConnCh
   672  	edsb.UpdateSubConnState(scToRemove, balancer.SubConnState{ConnectivityState: connectivity.Shutdown})
   673  
   674  	addrs2 := <-cc.NewSubConnAddrsCh
   675  	if got, want := addrs2[0].Addr, testEndpointAddrs[1]; got != want {
   676  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   677  	}
   678  	sc2 := <-cc.NewSubConnCh
   679  
   680  	// p1 is ready.
   681  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   682  	edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   683  
   684  	// Test roundrobin with only p1 subconns.
   685  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil {
   686  		t.Fatal(err)
   687  	}
   688  }
   689  
   690  // Test the case where the first and only priority is removed.
   691  func (s) TestEDSPriority_FirstPriorityRemoved(t *testing.T) {
   692  	const testPriorityInitTimeout = time.Second
   693  	defer func() func() {
   694  		old := priority.DefaultPriorityInitTimeout
   695  		priority.DefaultPriorityInitTimeout = testPriorityInitTimeout
   696  		return func() {
   697  			priority.DefaultPriorityInitTimeout = old
   698  		}
   699  	}()()
   700  
   701  	_, cc, xdsC, cleanup := setupTestEDS(t, nil)
   702  	defer cleanup()
   703  	// One localities, with priorities [0], each with one backend.
   704  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   705  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   706  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
   707  	// Remove the only localities.
   708  	clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   709  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil)
   710  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   711  	defer cancel()
   712  	if err := cc.WaitForErrPicker(ctx); err != nil {
   713  		t.Fatal(err)
   714  	}
   715  }
   716  
   717  // Watch resources from EDS and DNS, with EDS as the higher priority. Lower
   718  // priority is used when higher priority is not ready.
   719  func (s) TestFallbackToDNS(t *testing.T) {
   720  	const testDNSEndpointAddr = "3.1.4.1:5"
   721  	// dnsTargetCh, dnsCloseCh, resolveNowCh, dnsR, cleanup := setupDNS()
   722  	dnsTargetCh, _, resolveNowCh, dnsR, cleanupDNS := setupDNS()
   723  	defer cleanupDNS()
   724  	edsb, cc, xdsC, cleanup := setupTestEDS(t, nil)
   725  	defer cleanup()
   726  
   727  	if err := edsb.UpdateClientConnState(balancer.ClientConnState{
   728  		BalancerConfig: &LBConfig{
   729  			DiscoveryMechanisms: []DiscoveryMechanism{
   730  				{
   731  					Type:    DiscoveryMechanismTypeEDS,
   732  					Cluster: testClusterName,
   733  				},
   734  				{
   735  					Type:        DiscoveryMechanismTypeLogicalDNS,
   736  					DNSHostname: testDNSTarget,
   737  				},
   738  			},
   739  		},
   740  	}); err != nil {
   741  		t.Fatal(err)
   742  	}
   743  
   744  	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   745  	defer ctxCancel()
   746  	select {
   747  	case target := <-dnsTargetCh:
   748  		// Target.Scheme、Target.Endpoint are deprecated, use URL.Scheme、URL.Path instead.
   749  		//if diff := cmp.Diff(target, resolver.Target{Scheme: "dns", Endpoint: testDNSTarget}); diff != "" {
   750  		if diff := cmp.Diff(target, resolver.Target{URL: url.URL{Scheme: "dns", Path: testDNSTarget}}); diff != "" {
   751  			t.Fatalf("got unexpected DNS target to watch, diff (-got, +want): %v", diff)
   752  		}
   753  	case <-ctx.Done():
   754  		t.Fatal("Timed out waiting for building DNS resolver")
   755  	}
   756  
   757  	// One locality with one backend.
   758  	clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
   759  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
   760  	xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil)
   761  
   762  	// Also send a DNS update, because the balancer needs both updates from all
   763  	// resources to move on.
   764  	dnsR.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: testDNSEndpointAddr}}})
   765  
   766  	addrs0 := <-cc.NewSubConnAddrsCh
   767  	if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want {
   768  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   769  	}
   770  	sc0 := <-cc.NewSubConnCh
   771  
   772  	// p0 is ready.
   773  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   774  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   775  
   776  	// Test roundrobin with only p0 subconns.
   777  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil {
   778  		t.Fatal(err)
   779  	}
   780  
   781  	// Turn down 0, p1 (DNS) will be used.
   782  	edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   783  
   784  	// The transient failure above should not trigger a re-resolve to the DNS
   785  	// resolver. Need to read to clear the channel, to avoid potential deadlock
   786  	// writing to the channel later.
   787  	shortCtx, shortCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   788  	defer shortCancel()
   789  	select {
   790  	case <-resolveNowCh:
   791  		t.Fatal("unexpected re-resolve trigger by transient failure from EDS endpoint")
   792  	case <-shortCtx.Done():
   793  	}
   794  
   795  	// The addresses used to create new SubConn should be the DNS endpoint.
   796  	addrs1 := <-cc.NewSubConnAddrsCh
   797  	if got, want := addrs1[0].Addr, testDNSEndpointAddr; got != want {
   798  		t.Fatalf("sc is created with addr %v, want %v", got, want)
   799  	}
   800  	sc1 := <-cc.NewSubConnCh
   801  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
   802  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
   803  
   804  	// Test pick with 1.
   805  	if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil {
   806  		t.Fatal(err)
   807  	}
   808  
   809  	// Turn down the DNS endpoint, this should trigger an re-resolve in the DNS
   810  	// resolver.
   811  	edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
   812  
   813  	// The transient failure above should trigger a re-resolve to the DNS
   814  	// resolver. Need to read to clear the channel, to avoid potential deadlock
   815  	// writing to the channel later.
   816  	select {
   817  	case <-resolveNowCh:
   818  	case <-ctx.Done():
   819  		t.Fatal("Timed out waiting for re-resolve")
   820  	}
   821  }