milesibastos / jTDS

jTDS JDBC Driver
http://jtds.sourceforge.net/
GNU Lesser General Public License v2.1
81 stars 46 forks source link

The driver should support more than one A record pr host name #20

Open johnny-egeland opened 3 years ago

johnny-egeland commented 3 years ago

In High Availability setups, there are often two servers set up in a fail-over mode. This means that if one fails, the other immediately takes over. In such cases the DNS server is set up to provide two IP addresses for a DB server hostname. It is then up to the client (driver) to attempt to connect to all provided addresses before reporting a failure to connect.

This is currently not supported by jTDS, meaning it is cumbersome to use in such environments (I need to check which server is up, and change a raw IP in the settings manually). The following patch fixes this issue (however it's quite rudamentary). I'd suggest making this behavior optional, but the patch solves the issue locally for me.

diff --git a/src/main/net/sourceforge/jtds/jdbc/SharedSocket.java b/src/main/net/sourceforge/jtds/jdbc/SharedSocket.java
index d7bcd38..02caca7 100644
--- a/src/main/net/sourceforge/jtds/jdbc/SharedSocket.java
+++ b/src/main/net/sourceforge/jtds/jdbc/SharedSocket.java
@@ -25,6 +25,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.RandomAccessFile;
+import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.NetworkInterface;
 import java.net.Socket;
@@ -275,18 +276,34 @@ class SharedSocket {
       final String bindAddress = connection.getBindAddress();
       final int loginTimeout = connection.getLoginTimeout();

-      Socket socket = new Socket();
-      InetSocketAddress address = new InetSocketAddress( host, port );
+      // Resolve all IPs for configured hostname
+      InetAddress[] resolvedAddresses = InetAddress.getAllByName(host);

       // call Socket.bind(SocketAddress) if bindAddress parameter is set
-      if( bindAddress != null && ! bindAddress.isEmpty() )
+      String lastExceptionMessage = null;
+      for (InetAddress inetAddress : resolvedAddresses)
       {
-         socket.bind( new InetSocketAddress( bindAddress, 0 ) );
+         try
+         {
+            Socket socket = new Socket();
+            if( bindAddress != null && ! bindAddress.isEmpty() )
+            {
+               socket.bind( new InetSocketAddress( bindAddress, 0 ) );
+            }
+
+            // Try to connect to each of the given addresses
+            InetSocketAddress address = new InetSocketAddress(inetAddress, port);
+            socket.connect(address, loginTimeout * 1000);
+            return socket;
+         }
+         catch (IOException ex)
+         {
+             // NO-OP: Continue
+            lastExceptionMessage = ex.getMessage();
+         }
       }

-      // establish connection
-      socket.connect( address, loginTimeout * 1000 );
-      return socket;
+      throw new IOException("Unable to connect to SQL server. Last error: " + lastExceptionMessage);
    }

    String getMAC()