mirror of https://github.com/grpc/grpc-go.git
				
				
				
			Merge pull request #712 from iamqizhao/master
[Notice] Change the semantics of grpc.WithTimeout DialOption
This commit is contained in:
		
						commit
						b60d3e9ed8
					
				| 
						 | 
					@ -53,6 +53,9 @@ var (
 | 
				
			||||||
	// ErrClientConnClosing indicates that the operation is illegal because
 | 
						// ErrClientConnClosing indicates that the operation is illegal because
 | 
				
			||||||
	// the ClientConn is closing.
 | 
						// the ClientConn is closing.
 | 
				
			||||||
	ErrClientConnClosing = errors.New("grpc: the client connection is closing")
 | 
						ErrClientConnClosing = errors.New("grpc: the client connection is closing")
 | 
				
			||||||
 | 
						// ErrClientConnTimeout indicates that the ClientConn cannot establish the
 | 
				
			||||||
 | 
						// underlying connections within the specified timeout.
 | 
				
			||||||
 | 
						ErrClientConnTimeout = errors.New("grpc: timed out when dialing")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// errNoTransportSecurity indicates that there is no transport security
 | 
						// errNoTransportSecurity indicates that there is no transport security
 | 
				
			||||||
	// being set for ClientConn. Users should either set one or explicitly
 | 
						// being set for ClientConn. Users should either set one or explicitly
 | 
				
			||||||
| 
						 | 
					@ -62,15 +65,13 @@ var (
 | 
				
			||||||
	// (e.g., oauth2 token) which requires secure connection on an insecure
 | 
						// (e.g., oauth2 token) which requires secure connection on an insecure
 | 
				
			||||||
	// connection.
 | 
						// connection.
 | 
				
			||||||
	errCredentialsMisuse = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportAuthenticator() to set)")
 | 
						errCredentialsMisuse = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportAuthenticator() to set)")
 | 
				
			||||||
	// errClientConnTimeout indicates that the connection could not be
 | 
					 | 
				
			||||||
	// established or re-established within the specified timeout.
 | 
					 | 
				
			||||||
	errClientConnTimeout = errors.New("grpc: timed out trying to connect")
 | 
					 | 
				
			||||||
	// errNetworkIP indicates that the connection is down due to some network I/O error.
 | 
						// errNetworkIP indicates that the connection is down due to some network I/O error.
 | 
				
			||||||
	errNetworkIO = errors.New("grpc: failed with network I/O error")
 | 
						errNetworkIO = errors.New("grpc: failed with network I/O error")
 | 
				
			||||||
	// errConnDrain indicates that the connection starts to be drained and does not accept any new RPCs.
 | 
						// errConnDrain indicates that the connection starts to be drained and does not accept any new RPCs.
 | 
				
			||||||
	errConnDrain = errors.New("grpc: the connection is drained")
 | 
						errConnDrain = errors.New("grpc: the connection is drained")
 | 
				
			||||||
	// errConnClosing indicates that the connection is closing.
 | 
						// errConnClosing indicates that the connection is closing.
 | 
				
			||||||
	errConnClosing = errors.New("grpc: the connection is closing")
 | 
						errConnClosing = errors.New("grpc: the connection is closing")
 | 
				
			||||||
 | 
						errNoAddr      = errors.New("grpc: there is no address available to dial")
 | 
				
			||||||
	// minimum time to give a connection to complete
 | 
						// minimum time to give a connection to complete
 | 
				
			||||||
	minConnectTimeout = 20 * time.Second
 | 
						minConnectTimeout = 20 * time.Second
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -85,6 +86,7 @@ type dialOptions struct {
 | 
				
			||||||
	balancer Balancer
 | 
						balancer Balancer
 | 
				
			||||||
	block    bool
 | 
						block    bool
 | 
				
			||||||
	insecure bool
 | 
						insecure bool
 | 
				
			||||||
 | 
						timeout  time.Duration
 | 
				
			||||||
	copts    transport.ConnectOptions
 | 
						copts    transport.ConnectOptions
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -182,10 +184,11 @@ func WithPerRPCCredentials(creds credentials.Credentials) DialOption {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithTimeout returns a DialOption that configures a timeout for dialing a client connection.
 | 
					// WithTimeout returns a DialOption that configures a timeout for dialing a ClientConn
 | 
				
			||||||
 | 
					// initially. This is valid if and only if WithBlock() is present.
 | 
				
			||||||
func WithTimeout(d time.Duration) DialOption {
 | 
					func WithTimeout(d time.Duration) DialOption {
 | 
				
			||||||
	return func(o *dialOptions) {
 | 
						return func(o *dialOptions) {
 | 
				
			||||||
		o.copts.Timeout = d
 | 
							o.timeout = d
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -228,26 +231,47 @@ func Dial(target string, opts ...DialOption) (*ClientConn, error) {
 | 
				
			||||||
	if err := cc.balancer.Start(target); err != nil {
 | 
						if err := cc.balancer.Start(target); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							ok    bool
 | 
				
			||||||
 | 
							addrs []Address
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
	ch := cc.balancer.Notify()
 | 
						ch := cc.balancer.Notify()
 | 
				
			||||||
	if ch == nil {
 | 
						if ch == nil {
 | 
				
			||||||
		// There is no name resolver installed.
 | 
							// There is no name resolver installed.
 | 
				
			||||||
		addr := Address{Addr: target}
 | 
							addrs = append(addrs, Address{Addr: target})
 | 
				
			||||||
		if err := cc.newAddrConn(addr, false); err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		addrs, ok := <-ch
 | 
							addrs, ok = <-ch
 | 
				
			||||||
		if !ok || len(addrs) == 0 {
 | 
							if !ok || len(addrs) == 0 {
 | 
				
			||||||
			return nil, fmt.Errorf("grpc: there is no address available to dial")
 | 
								return nil, errNoAddr
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						waitC := make(chan error)
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
		for _, a := range addrs {
 | 
							for _, a := range addrs {
 | 
				
			||||||
			if err := cc.newAddrConn(a, false); err != nil {
 | 
								if err := cc.newAddrConn(a, false); err != nil {
 | 
				
			||||||
				return nil, err
 | 
									waitC <- err
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							close(waitC)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						var timeoutCh <-chan time.Time
 | 
				
			||||||
 | 
						if cc.dopts.timeout > 0 {
 | 
				
			||||||
 | 
							timeoutCh = time.After(cc.dopts.timeout)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case err := <-waitC:
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								cc.Close()
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case <-timeoutCh:
 | 
				
			||||||
 | 
							cc.Close()
 | 
				
			||||||
 | 
							return nil, ErrClientConnTimeout
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
		go cc.lbWatcher()
 | 
							go cc.lbWatcher()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	colonPos := strings.LastIndex(target, ":")
 | 
						colonPos := strings.LastIndex(target, ":")
 | 
				
			||||||
	if colonPos == -1 {
 | 
						if colonPos == -1 {
 | 
				
			||||||
		colonPos = len(target)
 | 
							colonPos = len(target)
 | 
				
			||||||
| 
						 | 
					@ -517,7 +541,6 @@ func (ac *addrConn) waitForStateChange(ctx context.Context, sourceState Connecti
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ac *addrConn) resetTransport(closeTransport bool) error {
 | 
					func (ac *addrConn) resetTransport(closeTransport bool) error {
 | 
				
			||||||
	var retries int
 | 
						var retries int
 | 
				
			||||||
	start := time.Now()
 | 
					 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		ac.mu.Lock()
 | 
							ac.mu.Lock()
 | 
				
			||||||
		ac.printf("connecting")
 | 
							ac.printf("connecting")
 | 
				
			||||||
| 
						 | 
					@ -537,29 +560,13 @@ func (ac *addrConn) resetTransport(closeTransport bool) error {
 | 
				
			||||||
		if closeTransport && t != nil {
 | 
							if closeTransport && t != nil {
 | 
				
			||||||
			t.Close()
 | 
								t.Close()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// Adjust timeout for the current try.
 | 
					 | 
				
			||||||
		copts := ac.dopts.copts
 | 
					 | 
				
			||||||
		if copts.Timeout < 0 {
 | 
					 | 
				
			||||||
			ac.tearDown(errClientConnTimeout)
 | 
					 | 
				
			||||||
			return errClientConnTimeout
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if copts.Timeout > 0 {
 | 
					 | 
				
			||||||
			copts.Timeout -= time.Since(start)
 | 
					 | 
				
			||||||
			if copts.Timeout <= 0 {
 | 
					 | 
				
			||||||
				ac.tearDown(errClientConnTimeout)
 | 
					 | 
				
			||||||
				return errClientConnTimeout
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		sleepTime := ac.dopts.bs.backoff(retries)
 | 
							sleepTime := ac.dopts.bs.backoff(retries)
 | 
				
			||||||
		timeout := sleepTime
 | 
							ac.dopts.copts.Timeout = sleepTime
 | 
				
			||||||
		if timeout < minConnectTimeout {
 | 
							if sleepTime < minConnectTimeout {
 | 
				
			||||||
			timeout = minConnectTimeout
 | 
								ac.dopts.copts.Timeout = minConnectTimeout
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if copts.Timeout == 0 || copts.Timeout > timeout {
 | 
					 | 
				
			||||||
			copts.Timeout = timeout
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		connectTime := time.Now()
 | 
							connectTime := time.Now()
 | 
				
			||||||
		newTransport, err := transport.NewClientTransport(ac.addr.Addr, &copts)
 | 
							newTransport, err := transport.NewClientTransport(ac.addr.Addr, &ac.dopts.copts)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ac.mu.Lock()
 | 
								ac.mu.Lock()
 | 
				
			||||||
			if ac.state == Shutdown {
 | 
								if ac.state == Shutdown {
 | 
				
			||||||
| 
						 | 
					@ -579,14 +586,6 @@ func (ac *addrConn) resetTransport(closeTransport bool) error {
 | 
				
			||||||
			if sleepTime < 0 {
 | 
								if sleepTime < 0 {
 | 
				
			||||||
				sleepTime = 0
 | 
									sleepTime = 0
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// Fail early before falling into sleep.
 | 
					 | 
				
			||||||
			if ac.dopts.copts.Timeout > 0 && ac.dopts.copts.Timeout < sleepTime+time.Since(start) {
 | 
					 | 
				
			||||||
				ac.mu.Lock()
 | 
					 | 
				
			||||||
				ac.errorf("connection timeout")
 | 
					 | 
				
			||||||
				ac.mu.Unlock()
 | 
					 | 
				
			||||||
				ac.tearDown(errClientConnTimeout)
 | 
					 | 
				
			||||||
				return errClientConnTimeout
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			closeTransport = false
 | 
								closeTransport = false
 | 
				
			||||||
			select {
 | 
								select {
 | 
				
			||||||
			case <-time.After(sleepTime):
 | 
								case <-time.After(sleepTime):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,8 +47,8 @@ func TestDialTimeout(t *testing.T) {
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		conn.Close()
 | 
							conn.Close()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err != errClientConnTimeout {
 | 
						if err != ErrClientConnTimeout {
 | 
				
			||||||
		t.Fatalf("Dial(_, _) = %v, %v, want %v", conn, err, errClientConnTimeout)
 | 
							t.Fatalf("Dial(_, _) = %v, %v, want %v", conn, err, ErrClientConnTimeout)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,8 +61,8 @@ func TestTLSDialTimeout(t *testing.T) {
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		conn.Close()
 | 
							conn.Close()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err != errClientConnTimeout {
 | 
						if err != ErrClientConnTimeout {
 | 
				
			||||||
		t.Fatalf("grpc.Dial(_, _) = %v, %v, want %v", conn, err, errClientConnTimeout)
 | 
							t.Fatalf("grpc.Dial(_, _) = %v, %v, want %v", conn, err, ErrClientConnTimeout)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -296,61 +296,6 @@ func (s *testServer) HalfDuplexCall(stream testpb.TestService_HalfDuplexCallServ
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const tlsDir = "testdata/"
 | 
					const tlsDir = "testdata/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestReconnectTimeout(t *testing.T) {
 | 
					 | 
				
			||||||
	defer leakCheck(t)()
 | 
					 | 
				
			||||||
	restore := declareLogNoise(t,
 | 
					 | 
				
			||||||
		"transport: http2Client.notifyError got notified that the client transport was broken",
 | 
					 | 
				
			||||||
		"grpc: Conn.resetTransport failed to create client transport: connection error: desc = \"transport",
 | 
					 | 
				
			||||||
		"grpc: Conn.transportMonitor exits due to: grpc: timed out trying to connect",
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	defer restore()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	lis, err := net.Listen("tcp", "localhost:0")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("Failed to listen: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, port, err := net.SplitHostPort(lis.Addr().String())
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("Failed to parse listener address: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	addr := "localhost:" + port
 | 
					 | 
				
			||||||
	conn, err := grpc.Dial(addr, grpc.WithTimeout(5*time.Second), grpc.WithBlock(), grpc.WithInsecure())
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("Failed to dial to the server %q: %v", addr, err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Close unaccepted connection (i.e., conn).
 | 
					 | 
				
			||||||
	lis.Close()
 | 
					 | 
				
			||||||
	tc := testpb.NewTestServiceClient(conn)
 | 
					 | 
				
			||||||
	waitC := make(chan struct{})
 | 
					 | 
				
			||||||
	go func() {
 | 
					 | 
				
			||||||
		defer close(waitC)
 | 
					 | 
				
			||||||
		const argSize = 271828
 | 
					 | 
				
			||||||
		const respSize = 314159
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, argSize)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Error(err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		req := &testpb.SimpleRequest{
 | 
					 | 
				
			||||||
			ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
 | 
					 | 
				
			||||||
			ResponseSize: proto.Int32(respSize),
 | 
					 | 
				
			||||||
			Payload:      payload,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		ctx, _ := context.WithTimeout(context.Background(), 10*time.Millisecond)
 | 
					 | 
				
			||||||
		if _, err := tc.UnaryCall(ctx, req); err == nil {
 | 
					 | 
				
			||||||
			t.Errorf("TestService/UnaryCall(_, _) = _, <nil>, want _, non-nil")
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
	// Block until reconnect times out.
 | 
					 | 
				
			||||||
	<-waitC
 | 
					 | 
				
			||||||
	if err := conn.Close(); err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("%v.Close() = %v, want <nil>", conn, err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func unixDialer(addr string, timeout time.Duration) (net.Conn, error) {
 | 
					func unixDialer(addr string, timeout time.Duration) (net.Conn, error) {
 | 
				
			||||||
	return net.DialTimeout("unix", addr, timeout)
 | 
						return net.DialTimeout("unix", addr, timeout)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -338,7 +338,7 @@ type ConnectOptions struct {
 | 
				
			||||||
	Dialer func(string, time.Duration) (net.Conn, error)
 | 
						Dialer func(string, time.Duration) (net.Conn, error)
 | 
				
			||||||
	// AuthOptions stores the credentials required to setup a client connection and/or issue RPCs.
 | 
						// AuthOptions stores the credentials required to setup a client connection and/or issue RPCs.
 | 
				
			||||||
	AuthOptions []credentials.Credentials
 | 
						AuthOptions []credentials.Credentials
 | 
				
			||||||
	// Timeout specifies the timeout for dialing a client connection.
 | 
						// Timeout specifies the timeout for dialing a ClientTransport.
 | 
				
			||||||
	Timeout time.Duration
 | 
						Timeout time.Duration
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue