diff --git a/clientv3/client.go b/clientv3/client.go index 160499fdd..d4a7d3410 100644 --- a/clientv3/client.go +++ b/clientv3/client.go @@ -294,8 +294,16 @@ func (c *Client) dial(endpoint string, dopts ...grpc.DialOption) (*grpc.ClientCo tokenMu: &sync.RWMutex{}, } - err := c.getToken(c.ctx) - if err != nil { + ctx := c.ctx + if c.cfg.DialTimeout > 0 { + cctx, cancel := context.WithTimeout(ctx, c.cfg.DialTimeout) + defer cancel() + ctx = cctx + } + if err := c.getToken(ctx); err != nil { + if err == ctx.Err() && ctx.Err() != c.ctx.Err() { + err = grpc.ErrClientConnTimeout + } return nil, err } @@ -351,6 +359,8 @@ func newClient(cfg *Config) (*Client, error) { client.balancer = newSimpleBalancer(cfg.Endpoints) conn, err := client.dial(cfg.Endpoints[0], grpc.WithBalancer(client.balancer)) if err != nil { + client.cancel() + client.balancer.Close() return nil, err } client.conn = conn @@ -374,6 +384,7 @@ func newClient(cfg *Config) (*Client, error) { default: } client.cancel() + client.balancer.Close() conn.Close() return nil, err } diff --git a/clientv3/client_test.go b/clientv3/client_test.go index 8a61aba5f..db863a2c6 100644 --- a/clientv3/client_test.go +++ b/clientv3/client_test.go @@ -81,33 +81,45 @@ func TestDialCancel(t *testing.T) { func TestDialTimeout(t *testing.T) { defer testutil.AfterTest(t) - donec := make(chan error) - go func() { - // without timeout, dial continues forever on ipv4 blackhole - cfg := Config{ + testCfgs := []Config{ + Config{ Endpoints: []string{"http://254.0.0.1:12345"}, - DialTimeout: 2 * time.Second} - c, err := New(cfg) - if c != nil || err == nil { - t.Errorf("new client should fail") - } - donec <- err - }() - - time.Sleep(10 * time.Millisecond) - - select { - case err := <-donec: - t.Errorf("dial didn't wait (%v)", err) - default: + DialTimeout: 2 * time.Second, + }, + Config{ + Endpoints: []string{"http://254.0.0.1:12345"}, + DialTimeout: time.Second, + Username: "abc", + Password: "def", + }, } - select { - case <-time.After(5 * time.Second): - t.Errorf("failed to timeout dial on time") - case err := <-donec: - if err != grpc.ErrClientConnTimeout { - t.Errorf("unexpected error %v, want %v", err, grpc.ErrClientConnTimeout) + for i, cfg := range testCfgs { + donec := make(chan error) + go func() { + // without timeout, dial continues forever on ipv4 blackhole + c, err := New(cfg) + if c != nil || err == nil { + t.Errorf("#%d: new client should fail", i) + } + donec <- err + }() + + time.Sleep(10 * time.Millisecond) + + select { + case err := <-donec: + t.Errorf("#%d: dial didn't wait (%v)", i, err) + default: + } + + select { + case <-time.After(5 * time.Second): + t.Errorf("#%d: failed to timeout dial on time", i) + case err := <-donec: + if err != grpc.ErrClientConnTimeout { + t.Errorf("#%d: unexpected error %v, want %v", i, err, grpc.ErrClientConnTimeout) + } } } }