瀏覽代碼

Connect to multiple hosts behind a DNS name (#1022)

When using a DNS name as your connection point, this allows the client to attempt to connect to multiple nodes in the cluster when creating the control connection.

We've encountered an issue where our DNS will sometimes return a temporarily offline host first from DNS.  During this time, any client that attempts to bootstrap will return an error as it will only try to connect to that host.  Since we return many hosts in our DNS, the client should attempt to connect to several of them, as if we had passed in a list of IP addresses.
Seth Rosenblum 8 年之前
父節點
當前提交
1762cce65e
共有 4 個文件被更改,包括 35 次插入27 次删除
  1. 2 2
      conn_test.go
  2. 29 22
      control.go
  3. 2 1
      control_test.go
  4. 2 2
      session.go

+ 2 - 2
conn_test.go

@@ -732,11 +732,11 @@ func (srv *TestServer) session() (*Session, error) {
 }
 }
 
 
 func (srv *TestServer) host() *HostInfo {
 func (srv *TestServer) host() *HostInfo {
-	host, err := hostInfo(srv.Address, 9042)
+	hosts, err := hostInfo(srv.Address, 9042)
 	if err != nil {
 	if err != nil {
 		srv.t.Fatal(err)
 		srv.t.Fatal(err)
 	}
 	}
-	return host
+	return hosts[0]
 }
 }
 
 
 func (srv *TestServer) closeWatch() {
 func (srv *TestServer) closeWatch() {

+ 29 - 22
control.go

@@ -99,7 +99,7 @@ func (c *controlConn) heartBeat() {
 
 
 var hostLookupPreferV4 = os.Getenv("GOCQL_HOST_LOOKUP_PREFER_V4") == "true"
 var hostLookupPreferV4 = os.Getenv("GOCQL_HOST_LOOKUP_PREFER_V4") == "true"
 
 
-func hostInfo(addr string, defaultPort int) (*HostInfo, error) {
+func hostInfo(addr string, defaultPort int) ([]*HostInfo, error) {
 	var port int
 	var port int
 	host, portStr, err := net.SplitHostPort(addr)
 	host, portStr, err := net.SplitHostPort(addr)
 	if err != nil {
 	if err != nil {
@@ -112,33 +112,40 @@ func hostInfo(addr string, defaultPort int) (*HostInfo, error) {
 		}
 		}
 	}
 	}
 
 
-	ip := net.ParseIP(host)
-	if ip == nil {
-		ips, err := net.LookupIP(host)
-		if err != nil {
-			return nil, err
-		} else if len(ips) == 0 {
-			return nil, fmt.Errorf("No IP's returned from DNS lookup for %q", addr)
-		}
+	var hosts []*HostInfo
 
 
-		if hostLookupPreferV4 {
-			for _, v := range ips {
-				if v4 := v.To4(); v4 != nil {
-					ip = v4
-					break
-				}
-			}
-			if ip == nil {
-				ip = ips[0]
+	// Check if host is a literal IP address
+	if ip := net.ParseIP(host); ip != nil {
+		hosts = append(hosts, &HostInfo{connectAddress: ip, port: port})
+		return hosts, nil
+	}
+
+	// Look up host in DNS
+	ips, err := net.LookupIP(host)
+	if err != nil {
+		return nil, err
+	} else if len(ips) == 0 {
+		return nil, fmt.Errorf("No IP's returned from DNS lookup for %q", addr)
+	}
+
+	// Filter to v4 addresses if any present
+	if hostLookupPreferV4 {
+		var preferredIPs []net.IP
+		for _, v := range ips {
+			if v4 := v.To4(); v4 != nil {
+				preferredIPs = append(preferredIPs, v4)
 			}
 			}
-		} else {
-			// TODO(zariel): should we check that we can connect to any of the ips?
-			ip = ips[0]
 		}
 		}
+		if len(preferredIPs) != 0 {
+			ips = preferredIPs
+		}
+	}
 
 
+	for _, ip := range ips {
+		hosts = append(hosts, &HostInfo{connectAddress: ip, port: port})
 	}
 	}
 
 
-	return &HostInfo{connectAddress: ip, port: port}, nil
+	return hosts, nil
 }
 }
 
 
 func shuffleHosts(hosts []*HostInfo) []*HostInfo {
 func shuffleHosts(hosts []*HostInfo) []*HostInfo {

+ 2 - 1
control_test.go

@@ -18,12 +18,13 @@ func TestHostInfo_Lookup(t *testing.T) {
 	}
 	}
 
 
 	for i, test := range tests {
 	for i, test := range tests {
-		host, err := hostInfo(test.addr, 1)
+		hosts, err := hostInfo(test.addr, 1)
 		if err != nil {
 		if err != nil {
 			t.Errorf("%d: %v", i, err)
 			t.Errorf("%d: %v", i, err)
 			continue
 			continue
 		}
 		}
 
 
+		host := hosts[0]
 		if !host.ConnectAddress().Equal(test.ip) {
 		if !host.ConnectAddress().Equal(test.ip) {
 			t.Errorf("expected ip %v got %v for addr %q", test.ip, host.ConnectAddress(), test.addr)
 			t.Errorf("expected ip %v got %v for addr %q", test.ip, host.ConnectAddress(), test.addr)
 		}
 		}

+ 2 - 2
session.go

@@ -78,7 +78,7 @@ var queryPool = &sync.Pool{
 func addrsToHosts(addrs []string, defaultPort int) ([]*HostInfo, error) {
 func addrsToHosts(addrs []string, defaultPort int) ([]*HostInfo, error) {
 	var hosts []*HostInfo
 	var hosts []*HostInfo
 	for _, hostport := range addrs {
 	for _, hostport := range addrs {
-		host, err := hostInfo(hostport, defaultPort)
+		resolvedHosts, err := hostInfo(hostport, defaultPort)
 		if err != nil {
 		if err != nil {
 			// Try other hosts if unable to resolve DNS name
 			// Try other hosts if unable to resolve DNS name
 			if _, ok := err.(*net.DNSError); ok {
 			if _, ok := err.(*net.DNSError); ok {
@@ -88,7 +88,7 @@ func addrsToHosts(addrs []string, defaultPort int) ([]*HostInfo, error) {
 			return nil, err
 			return nil, err
 		}
 		}
 
 
-		hosts = append(hosts, host)
+		hosts = append(hosts, resolvedHosts...)
 	}
 	}
 	if len(hosts) == 0 {
 	if len(hosts) == 0 {
 		return nil, errors.New("failed to resolve any of the provided hostnames")
 		return nil, errors.New("failed to resolve any of the provided hostnames")