Skip to content

Commit

Permalink
client/SubConn: do not recreate addrConn if UpdateAddresses is called…
Browse files Browse the repository at this point in the history
… with the same addresses (#5373)
  • Loading branch information
menghanl authored May 20, 2022
1 parent 459729d commit 30b9d59
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 10 deletions.
25 changes: 22 additions & 3 deletions clientconn.go
Original file line number Diff line number Diff line change
Expand Up @@ -801,16 +801,31 @@ func (ac *addrConn) connect() error {
return nil
}

func equalAddresses(a, b []resolver.Address) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if !v.Equal(b[i]) {
return false
}
}
return true
}

// tryUpdateAddrs tries to update ac.addrs with the new addresses list.
//
// If ac is Connecting, it returns false. The caller should tear down the ac and
// create a new one. Note that the backoff will be reset when this happens.
//
// If ac is TransientFailure, it updates ac.addrs and returns true. The updated
// addresses will be picked up by retry in the next iteration after backoff.
//
// If ac is Shutdown or Idle, it updates ac.addrs and returns true.
//
// If the addresses is the same as the old list, it does nothing and returns
// true.
//
// If ac is Connecting, it returns false. The caller should tear down the ac and
// create a new one. Note that the backoff will be reset when this happens.
//
// If ac is Ready, it checks whether current connected address of ac is in the
// new addrs list.
// - If true, it updates ac.addrs and returns true. The ac will keep using
Expand All @@ -827,6 +842,10 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
return true
}

if equalAddresses(ac.addrs, addrs) {
return true
}

if ac.state == connectivity.Connecting {
return false
}
Expand Down
16 changes: 9 additions & 7 deletions clientconn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -854,9 +854,10 @@ func (s) TestBackoffCancel(t *testing.T) {
}
}

// UpdateAddresses should cause the next reconnect to begin from the top of the
// list if the connection is not READY.
func (s) TestUpdateAddresses_RetryFromFirstAddr(t *testing.T) {
// TestUpdateAddresses_NoopIfCalledWithSameAddresses tests that UpdateAddresses
// should be noop if UpdateAddresses is called with the same list of addresses,
// even when the SubConn is in Connecting and doesn't have a current address.
func (s) TestUpdateAddresses_NoopIfCalledWithSameAddresses(t *testing.T) {
lis1, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("Error while listening. Err: %v", err)
Expand Down Expand Up @@ -1008,19 +1009,20 @@ func (s) TestUpdateAddresses_RetryFromFirstAddr(t *testing.T) {
}
client.mu.Unlock()

// Call UpdateAddresses with the same list of addresses, it should be a noop
// (even when the SubConn is Connecting, and doesn't have a curAddr).
ac.acbw.UpdateAddresses(addrsList)

// We've called tryUpdateAddrs - now let's make server2 close the
// connection and check that it goes back to server1 instead of continuing
// to server3 or trying server2 again.
// connection and check that it continues to server3.
close(closeServer2)

select {
case <-server1ContactedSecondTime:
t.Fatal("server1 was contacted a second time, but it should have continued to server 3")
case <-server2ContactedSecondTime:
t.Fatal("server2 was contacted a second time, but it after tryUpdateAddrs it should have re-started the list and tried server1")
t.Fatal("server2 was contacted a second time, but it should have continued to server 3")
case <-server3Contacted:
t.Fatal("server3 was contacted, but after tryUpdateAddrs it should have re-started the list and tried server1")
case <-timeout:
t.Fatal("timed out waiting for any server to be contacted after tryUpdateAddrs")
}
Expand Down

0 comments on commit 30b9d59

Please sign in to comment.