=== modified file 'src/com/mysql/jdbc/LoadBalancingConnectionProxy.java' --- src/com/mysql/jdbc/LoadBalancingConnectionProxy.java 2009-01-13 23:55:53 +0000 +++ src/com/mysql/jdbc/LoadBalancingConnectionProxy.java 2009-03-06 20:55:43 +0000 @@ -318,7 +318,7 @@ } if ("close".equals(methodName)) { - synchronized (this.liveConnections) { + synchronized (this) { // close all underlying connections Iterator allConnections = this.liveConnections.values() .iterator(); @@ -474,10 +474,34 @@ public synchronized void doPing() throws SQLException { Iterator allConns = this.liveConnections.values().iterator(); - - while (allConns.hasNext()) { - ((Connection)allConns.next()).ping(); + if(this.isGlobalBlacklistEnabled()){ + SQLException se = null; + boolean foundHost = false; + synchronized(this){ + for(Iterator i = this.hostList.iterator(); i.hasNext(); ){ + String host = (String) i.next(); + Connection conn = (Connection) this.liveConnections.get(host); + if(conn == null){ + continue; + } + try{ + conn.ping(); + foundHost = true; + } catch (SQLException e){ + se = e; + this.addToGlobalBlacklist(host); + } + } + } + if(!foundHost){ + throw se; + } + } else { + while (allConns.hasNext()) { + ((Connection)allConns.next()).ping(); + } } + } public void addToGlobalBlacklist(String host) { === modified file 'src/testsuite/UnreliableSocketFactory.java' --- src/testsuite/UnreliableSocketFactory.java 2008-10-20 22:29:45 +0000 +++ src/testsuite/UnreliableSocketFactory.java 2009-03-06 20:10:08 +0000 @@ -63,6 +63,18 @@ static final Set IMMEDIATELY_DOWNED_HOSTS = new HashSet(); + private String hostname; + private int portNumber; + private Properties props; + + public static void flushAllHostLists(){ + IMMEDIATELY_DOWNED_HOSTS.clear(); + HUNG_CONNECT_HOSTS.clear(); + HUNG_READ_HOSTS.clear(); + HUNG_WRITE_HOSTS.clear(); + } + + public static void mapHost(String alias, String orig) { MAPPED_HOSTS.put(alias, orig); } @@ -102,6 +114,13 @@ public Socket connect(String hostname, int portNumber, Properties props) throws SocketException, IOException { + this.hostname = hostname; + this.portNumber = portNumber; + this.props = props; + return getNewSocket(); + } + + private Socket getNewSocket() throws SocketException, IOException { if (IMMEDIATELY_DOWNED_HOSTS.contains(hostname)) { sleepMillisForProperty(props, "connectTimeout"); @@ -117,6 +136,16 @@ return new HangingSocket(super.connect(hostnameToConnectTo, portNumber, props), props, hostname); } + + + + public Socket afterHandshake() throws SocketException, IOException { + return getNewSocket(); + } + + public Socket beforeHandshake() throws SocketException, IOException { + return getNewSocket(); + } static void sleepMillisForProperty(Properties props, String name) { try { @@ -364,7 +393,7 @@ } private void failIfRequired() throws SocketTimeoutException { - if (HUNG_READ_HOSTS.contains(aliasedHostname)) { + if (HUNG_READ_HOSTS.contains(aliasedHostname) || IMMEDIATELY_DOWNED_HOSTS.contains(aliasedHostname)) { sleepMillisForProperty(props, "socketTimeout"); throw new SocketTimeoutException(); @@ -416,7 +445,7 @@ } private void failIfRequired() throws SocketTimeoutException { - if (HUNG_WRITE_HOSTS.contains(aliasedHostname)) { + if (HUNG_WRITE_HOSTS.contains(aliasedHostname) || IMMEDIATELY_DOWNED_HOSTS.contains(aliasedHostname)) { sleepMillisForProperty(props, "socketTimeout"); throw new SocketTimeoutException(); === modified file 'src/testsuite/regression/ConnectionRegressionTest.java' --- src/testsuite/regression/ConnectionRegressionTest.java 2008-10-20 22:29:45 +0000 +++ src/testsuite/regression/ConnectionRegressionTest.java 2009-03-06 20:19:38 +0000 @@ -44,6 +44,7 @@ import java.util.StringTokenizer; import testsuite.BaseTestCase; +import testsuite.UnreliableSocketFactory; import com.mysql.jdbc.ConnectionImpl; import com.mysql.jdbc.Driver; @@ -2437,4 +2438,91 @@ } } } + public void testUnreliableSocketFactory() throws Exception { + Properties props = new Properties(); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + props.setProperty("loadBalanceStrategy", "bestResponseTime"); + NonRegisteringDriver d = new NonRegisteringDriver(); + String host = d.parseURL(BaseTestCase.dbUrl, props).getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + if(host == null){ + host = "localhost"; + } + + UnreliableSocketFactory.mapHost("first", host); + UnreliableSocketFactory.mapHost("second", host); + + Connection conn = getConnectionWithProps("jdbc:mysql:loadbalance://first,second/test", props); + assertNotNull("Connection should not be null", conn); + try { + conn.createStatement().execute("SELECT 1"); + conn.createStatement().execute("SELECT 1"); + // both connections are live now + UnreliableSocketFactory.downHost("first"); + UnreliableSocketFactory.downHost("second"); + try{ + conn.createStatement().execute("SELECT 1"); + fail("Should hang here."); + } catch (SQLException sqlEx){ + assertEquals("08S01", sqlEx.getSQLState()); + } + } finally { + closeMemberJDBCResources(); + } + } + + public void testBug43421() throws Exception { + Properties props = new Properties(); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + props.setProperty("bestResponse", "bestResponseTime"); + NonRegisteringDriver d = new NonRegisteringDriver(); + Properties testCaseProps = d.parseURL(BaseTestCase.dbUrl, null); + String host = testCaseProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + String db = testCaseProps.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if(host == null){ + host = "localhost"; + } + UnreliableSocketFactory.mapHost("first", host); + UnreliableSocketFactory.mapHost("second", host); + + Connection conn = getConnectionWithProps("jdbc:mysql:loadbalance://first,second/" + db, props); + assertNotNull("Connection should not be null", conn); + + try { + conn.createStatement().execute("SELECT 1"); + conn.createStatement().execute("SELECT 1"); + // both connections are live now + UnreliableSocketFactory.downHost("second"); + try{ + conn.createStatement().execute("/* ping */"); + fail("Pings will not succeed when one host is down and using loadbalance w/o global blacklist."); + } catch (SQLException sqlEx){ + } + } finally { + closeMemberJDBCResources(); + } + + UnreliableSocketFactory.flushAllHostLists(); + props = new Properties(); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + props.setProperty("loadBalanceStrategy", "bestResponseTime"); + props.setProperty("globalBlacklistTimeout", "200"); + + conn = getConnectionWithProps("jdbc:mysql:loadbalance://first,second/" + db, props); + assertNotNull("Connection should not be null", conn); + + try { + conn.createStatement().execute("SELECT 1"); + conn.createStatement().execute("SELECT 1"); + // both connections are live now + UnreliableSocketFactory.downHost("second"); + try{ + conn.createStatement().execute("/* ping */"); + } catch (SQLException sqlEx){ + fail("Pings should succeed even though host is down."); + } + } finally { + closeMemberJDBCResources(); + } + + } } \ No newline at end of file