diff --git a/clientv3/client.go b/clientv3/client.go index 7cfb0b0eb4a..ace40b45465 100644 --- a/clientv3/client.go +++ b/clientv3/client.go @@ -48,7 +48,9 @@ type Client struct { Auth Maintenance - conn *grpc.ClientConn + conn *grpc.ClientConn + dialerrc chan error + cfg Config creds *credentials.TransportCredentials balancer *simpleBalancer @@ -214,7 +216,14 @@ func (c *Client) dialSetupOpts(endpoint string, dopts ...grpc.DialOption) (opts default: } dialer := &net.Dialer{Timeout: t} - return dialer.DialContext(c.ctx, proto, host) + conn, err := dialer.DialContext(c.ctx, proto, host) + if err != nil { + select { + case c.dialerrc <- err: + default: + } + } + return conn, err } opts = append(opts, grpc.WithDialer(f)) @@ -316,11 +325,12 @@ func newClient(cfg *Config) (*Client, error) { ctx, cancel := context.WithCancel(baseCtx) client := &Client{ - conn: nil, - cfg: *cfg, - creds: creds, - ctx: ctx, - cancel: cancel, + conn: nil, + dialerrc: make(chan error, 1), + cfg: *cfg, + creds: creds, + ctx: ctx, + cancel: cancel, } if cfg.Username != "" && cfg.Password != "" { client.Username = cfg.Username @@ -347,9 +357,14 @@ func newClient(cfg *Config) (*Client, error) { case <-waitc: } if !hasConn { + err := grpc.ErrClientConnTimeout + select { + case err = <-client.dialerrc: + default: + } client.cancel() conn.Close() - return nil, grpc.ErrClientConnTimeout + return nil, err } } diff --git a/clientv3/client_test.go b/clientv3/client_test.go index 2b2b6d593ae..8aba025383d 100644 --- a/clientv3/client_test.go +++ b/clientv3/client_test.go @@ -72,9 +72,9 @@ func TestDialTimeout(t *testing.T) { donec := make(chan error) go func() { - // without timeout, grpc keeps redialing if connection refused + // without timeout, dial continues forever on ipv4 blackhole cfg := Config{ - Endpoints: []string{"localhost:12345"}, + Endpoints: []string{"http://254.0.0.1:12345"}, DialTimeout: 2 * time.Second} c, err := New(cfg) if c != nil || err == nil {