diff --git a/clientv3/balancer.go b/clientv3/balancer.go index 5ab9eb987..87137d4d5 100644 --- a/clientv3/balancer.go +++ b/clientv3/balancer.go @@ -124,7 +124,11 @@ func (b *simpleBalancer) updateAddrs(eps []string) { addrs = append(addrs, grpc.Address{Addr: getHost(eps[i])}) } b.addrs = addrs - b.notifyCh <- addrs + // updating notifyCh can trigger new connections, + // but balancer only expects new connections if all connections are down + if b.pinAddr == "" { + b.notifyCh <- addrs + } } func (b *simpleBalancer) Up(addr grpc.Address) func(error) { @@ -220,7 +224,7 @@ func (b *simpleBalancer) Close() error { close(b.notifyCh) b.pinAddr = "" - // In the case of follwing scenerio: + // In the case of following scenario: // 1. upc is not closed; no pinned address // 2. client issues an rpc, calling invoke(), which calls Get(), enters for loop, blocks // 3. clientconn.Close() calls balancer.Close(); closed = true diff --git a/clientv3/integration/dial_test.go b/clientv3/integration/dial_test.go index 9d9e6b47f..01e49675c 100644 --- a/clientv3/integration/dial_test.go +++ b/clientv3/integration/dial_test.go @@ -26,7 +26,16 @@ import ( ) // TestDialSetEndpoints ensures SetEndpoints can replace unavailable endpoints with available ones. -func TestDialSetEndpoints(t *testing.T) { +func TestDialSetEndpointsBeforeFail(t *testing.T) { + testDialSetEndpoints(t, true) +} + +func TestDialSetEndpointsAfterFail(t *testing.T) { + testDialSetEndpoints(t, false) +} + +// testDialSetEndpoints ensures SetEndpoints can replace unavailable endpoints with available ones. +func testDialSetEndpoints(t *testing.T, setBefore bool) { defer testutil.AfterTest(t) clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) defer clus.Terminate(t) @@ -45,13 +54,16 @@ func TestDialSetEndpoints(t *testing.T) { } defer cli.Close() + if setBefore { + cli.SetEndpoints(eps[toKill%3], eps[(toKill+1)%3]) + } // make a dead node clus.Members[toKill].Stop(t) clus.WaitLeader(t) - // update client with available endpoints - cli.SetEndpoints(eps[(toKill+1)%3]) - + if !setBefore { + cli.SetEndpoints(eps[toKill%3], eps[(toKill+1)%3]) + } ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) if _, err = cli.Get(ctx, "foo", clientv3.WithSerializable()); err != nil { t.Fatal(err)