From 38da5ce5bf7bde813c93dc08c53da17238f67f8f Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Tue, 17 Sep 2024 19:09:42 -0700 Subject: [PATCH 01/30] Fix for connectRetryCount > 0 --- .../sqlserver/jdbc/SQLServerConnection.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index d81e3da9e..b83f8f1cf 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -1994,7 +1994,7 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio if (0 == connectRetryCount) { // connection retry disabled throw e; - } else if (connectRetryAttempt++ > connectRetryCount) { + } else if (connectRetryAttempt++ >= connectRetryCount) { // maximum connection retry count reached if (connectionlogger.isLoggable(Level.FINE)) { connectionlogger.fine("Connection failed. Maximum connection retry count " @@ -2026,8 +2026,10 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio + sqlServerError.getErrorNumber() + ". Wait for connectRetryInterval(" + connectRetryInterval + ")s before retry."); } - - sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); + if (connectRetryAttempt > 1) { + // We do not sleep for first retry; first retry is immediate + sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); + } } } } @@ -3482,7 +3484,7 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu || timerHasExpired(timerExpire) // no time left || (timerRemaining(timerExpire) < TimeUnit.SECONDS.toMillis(connectRetryInterval) + 2 * timeForFirstTry) // not enough time for another retry - || (connectRetryCount == 0 && !isDBMirroring && !useTnir) // retries disabled + || (connectRetryCount == 0 && !(isDBMirroring || useTnir)) // retries disabled // retry at least once for TNIR and failover || (connectRetryCount == 0 && (isDBMirroring || useTnir) && attemptNumber > 0) || (connectRetryCount != 0 && attemptNumber >= connectRetryCount) // no retries left @@ -3542,7 +3544,9 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu + attemptNumber); } - sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); + + sleepForInterval(fedauthRetryInterval); + fedauthRetryInterval = (fedauthRetryInterval < 500) ? fedauthRetryInterval * 2 : 1000; } } From 2da9fa201c60c73524058c4d5a13aa9e50ecf5a1 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Tue, 17 Sep 2024 19:11:11 -0700 Subject: [PATCH 02/30] Cleanup --- .../java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index b83f8f1cf..99d80d526 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3484,7 +3484,7 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu || timerHasExpired(timerExpire) // no time left || (timerRemaining(timerExpire) < TimeUnit.SECONDS.toMillis(connectRetryInterval) + 2 * timeForFirstTry) // not enough time for another retry - || (connectRetryCount == 0 && !(isDBMirroring || useTnir)) // retries disabled + || (connectRetryCount == 0 && !isDBMirroring && !useTnir) // retries disabled // retry at least once for TNIR and failover || (connectRetryCount == 0 && (isDBMirroring || useTnir) && attemptNumber > 0) || (connectRetryCount != 0 && attemptNumber >= connectRetryCount) // no retries left @@ -3544,7 +3544,6 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu + attemptNumber); } - sleepForInterval(fedauthRetryInterval); fedauthRetryInterval = (fedauthRetryInterval < 500) ? fedauthRetryInterval * 2 : 1000; } From 0c624b9203386b1ffe72ad0115d267874ff572e6 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Tue, 17 Sep 2024 20:05:30 -0700 Subject: [PATCH 03/30] Added test to SQLServerConnectionTest + cleanup --- .../jdbc/SQLServerConnectionTest.java | 47 ++++++++------ .../sqlserver/jdbc/TestResource.java | 10 +-- .../jdbc/connection/TimeoutTest.java | 61 +++++++++---------- 3 files changed, 64 insertions(+), 54 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index 1ed9749cc..892d27de1 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -5,9 +5,10 @@ package com.microsoft.sqlserver.jdbc; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.io.Reader; @@ -193,7 +194,7 @@ public void testDataSource() throws SQLServerException { assertEquals("False", EncryptOption.valueOfString(ds.getEncrypt()).toString(), TestResource.getResource("R_valuesAreDifferent")); - // verify enrypt=strict options + // verify encrypt=strict options ds.setEncrypt(EncryptOption.STRICT.toString()); assertEquals("Strict", EncryptOption.valueOfString(ds.getEncrypt()).toString(), TestResource.getResource("R_valuesAreDifferent")); @@ -454,17 +455,27 @@ public void testConnectionPoolGetTwice() throws SQLException { } } + /** + * Runs the `testConnectCountInLoginAndCorrectRetryCount` test several times with different values of + * connectRetryCount. + */ + @Test + public void testConnectCountInLoginAndCorrectRetryCountForMultipleValues() { + testConnectCountInLoginAndCorrectRetryCount(0); + testConnectCountInLoginAndCorrectRetryCount(1); + testConnectCountInLoginAndCorrectRetryCount(2); + } + /** * Tests whether connectRetryCount and connectRetryInterval are properly respected in the login loop. As well, tests * that connection is retried the proper number of times. */ @Test - public void testConnectCountInLoginAndCorrectRetryCount() { + public void testConnectCountInLoginAndCorrectRetryCount(int connectRetryCount) { long timerStart = 0; - int connectRetryCount = 0; int connectRetryInterval = 60; - int longLoginTimeout = loginTimeOutInSeconds * 3; // 90 seconds + int longLoginTimeout = loginTimeOutInSeconds * 9; // 90 seconds try { SQLServerDataSource ds = new SQLServerDataSource(); @@ -476,7 +487,7 @@ public void testConnectCountInLoginAndCorrectRetryCount() { timerStart = System.currentTimeMillis(); try (Connection con = ds.getConnection()) { - assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); + assertNull(con, TestResource.getResource("R_shouldNotConnect")); } } catch (Exception e) { assertTrue( @@ -484,12 +495,15 @@ public void testConnectCountInLoginAndCorrectRetryCount() { || (TestUtils.getProperty(connectionString, "msiClientId") != null && (e.getMessage() .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase()) || e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_MInotAvailable").toLowerCase()))), + .contains(TestResource.getResource("R_MINotAvailable").toLowerCase()))), e.getMessage()); long totalTime = System.currentTimeMillis() - timerStart; // Maximum is unknown, but is needs to be less than longLoginTimeout or else this is an issue. assertTrue(totalTime < (longLoginTimeout * 1000L), TestResource.getResource("R_executionTooLong")); + // We should at least take as long as the retry interval between all retries past the first. + int minTimeInSecs = connectRetryInterval * (connectRetryCount - 1); + assertTrue(totalTime > (minTimeInSecs * 1000L), TestResource.getResource("R_executionNotLong")); } } @@ -763,7 +777,7 @@ public void testClientConnectionId() throws Exception { // Non-existent host, ClientConnectionId should not be available in error message try (Connection conn = PrepUtil.getConnection( - connectionString + ";instanceName=" + RandomUtil.getIdentifier("Instance") + ";logintimeout=5;")) { + connectionString + ";instanceName=" + RandomUtil.getIdentifier("Instance") + ";loginTimeout=5;")) { conn.close(); } catch (SQLException e) { @@ -800,9 +814,8 @@ public void testIncorrectDatabase() throws SQLException { } catch (Exception e) { assertTrue( e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase")) - || (TestUtils.getProperty(connectionString, "msiClientId") != null - && e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), + || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() + .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), e.getMessage()); timerEnd = System.currentTimeMillis(); } @@ -833,9 +846,8 @@ public void testIncorrectUserName() throws SQLException { } catch (Exception e) { assertTrue( e.getMessage().contains(TestResource.getResource("R_loginFailed")) - || (TestUtils.getProperty(connectionString, "msiClientId") != null - && e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), + || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() + .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), e.getMessage()); timerEnd = System.currentTimeMillis(); } @@ -866,9 +878,8 @@ public void testIncorrectPassword() throws SQLException { } catch (Exception e) { assertTrue( e.getMessage().contains(TestResource.getResource("R_loginFailed")) - || (TestUtils.getProperty(connectionString, "msiClientId") != null - && e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), + || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() + .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), e.getMessage()); timerEnd = System.currentTimeMillis(); } @@ -1283,7 +1294,7 @@ public void testServerNameField() throws SQLException { String[] serverNameAndPort = connectionString.substring(subProtocol.length(), indexOfFirstDelimiter).split(":"); String connectionProperties = connectionString.substring(indexOfFirstDelimiter, indexOfLastDelimiter + 1); - String loginTimeout = "loginTimout=15"; + String loginTimeout = "loginTimeout=15"; // Server name field is empty but serverName connection property is set, should pass String emptyServerNameField = subProtocol + connectionProperties + "serverName=" + serverNameAndPort[0] + ";"; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index 29a5e21dd..c20f63d7a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -30,7 +30,7 @@ protected Object[][] getContents() { // the keys must be prefixed with R_ to denote they are resource strings and their names should follow the // camelCasing // convention and be descriptive - static final Object[][] contents = {{"R_wrongEnv", "Aborting test: As this is not the right environement: "}, + static final Object[][] contents = {{"R_wrongEnv", "Aborting test: As this is not the right environment: "}, {"R_fipsPropertyNotSet", "Aborting test case as FIPS_ENV property is not set."}, {"R_invalidTrustCert", "Invalid TrustServerCertificate value."}, {"R_invalidEncrypt", "Invalid encrypt value."}, {"R_notImplemented", "not implemented"}, @@ -42,9 +42,11 @@ protected Object[][] getContents() { {"R_givenValueType", "The given value of type"}, {"R_lengthTruncated", " The inserted length is truncated or not correct!"}, {"R_timeValueTruncated", " The time value is truncated or not correct!"}, - {"R_nullPointerExceptionFromResultSet", "Cannot invoke \"java.sql.ResultSet.next()\" because \"rs\" is null"}, + {"R_nullPointerExceptionFromResultSet", + "Cannot invoke \"java.sql.ResultSet.next()\" because \"rs\" is null"}, {"R_invalidErrorMessage", "Invalid Error Message: "}, - {"R_kerberosNativeGSSFailure", "No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)"}, + {"R_kerberosNativeGSSFailure", + "No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)"}, {"R_expectedFailPassed", "Expected failure did not fail"}, {"R_dataTypeNotFound", "Cannot find data type"}, {"R_illegalCharWktPosition", "Illegal character in Well-Known text at position {0}."}, {"R_illegalCharWkt", "Illegal Well-Known text. Please make sure Well-Known text is valid."}, @@ -216,5 +218,5 @@ protected Object[][] getContents() { {"R_expectedClassDoesNotMatchActualClass", "Expected column class {0} does not match actual column class {1} for column {2}."}, {"R_loginFailedMI", "Login failed for user ''"}, - {"R_MInotAvailable", "Managed Identity authentication is not available"},}; + {"R_MINotAvailable", "Managed Identity authentication is not available"},}; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java index d7290b262..1e8fdff83 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java @@ -49,8 +49,11 @@ public static void setupTests() throws Exception { /* * TODO: * The tests below uses a simple interval counting logic to determine whether there was at least 1 retry. - * Given the interval is long enough, then 1 retry should take at least 1 interval long, so if it took < 1 interval, then it assumes there were no retry. However, this only works if TNIR or failover is not enabled since those cases should retry but no wait interval in between. So this interval counting can not detect these cases. - * Note a better and more reliable way would be to check attemptNumber using reflection to determine the number of retries. + * Given the interval is long enough, then 1 retry should take at least 1 interval long, so if it took < 1 interval, + * then it assumes there were no retry. However, this only works if TNIR or failover is not enabled since those cases + * should retry but no wait interval in between. So this interval counting can not detect these cases. + * Note a better and more reliable way would be to check attemptNumber using reflection to + * determine the number of retries. */ // test default loginTimeout used if not specified in connection string @@ -59,7 +62,7 @@ public void testDefaultLoginTimeout() { long totalTime = 0; long timerStart = System.currentTimeMillis(); - // non existing server and default values to see if took default timeout + // non-existing server and default values to see if took default timeout try (Connection con = PrepUtil.getConnection("jdbc:sqlserver://" + randomServer)) { fail(TestResource.getResource("R_shouldNotConnect")); } catch (Exception e) { @@ -77,7 +80,7 @@ public void testDefaultLoginTimeout() { // time should be < default loginTimeout assertTrue(totalTime < TimeUnit.SECONDS.toMillis(defaultTimeout), - "total time: " + totalTime + " default loginTimout: " + TimeUnit.SECONDS.toMillis(defaultTimeout)); + "total time: " + totalTime + " default loginTimeout: " + TimeUnit.SECONDS.toMillis(defaultTimeout)); } // test setting loginTimeout value @@ -88,8 +91,8 @@ public void testURLLoginTimeout() { long timerStart = System.currentTimeMillis(); - // non existing server and set loginTimeout - try (Connection con = PrepUtil.getConnection("jdbc:sqlserver://" + randomServer + ";logintimeout=" + timeout)) { + // non-existing server and set loginTimeout + try (Connection con = PrepUtil.getConnection("jdbc:sqlserver://" + randomServer + ";loginTimeout=" + timeout)) { fail(TestResource.getResource("R_shouldNotConnect")); } catch (Exception e) { totalTime = System.currentTimeMillis() - timerStart; @@ -180,7 +183,7 @@ public void testConnectRetryDisable() { int interval = defaultTimeout; // long interval so we can tell if there was a retry long timeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval - // non existent server with long loginTimeout, should return fast if no retries at all + // non-existent server with long loginTimeout, should return fast if no retries at all try (Connection con = PrepUtil.getConnection( "jdbc:sqlserver://" + randomServer + ";transparentNetworkIPResolution=false;loginTimeout=" + timeout + ";connectRetryCount=0;connectInterval=" + interval)) { @@ -209,7 +212,7 @@ public void testConnectRetryBadServer() { long timerStart = System.currentTimeMillis(); int timeout = 15; - // non existent server with very short loginTimeout, no retry will happen as not a transient error + // non-existent server with very short loginTimeout, no retry will happen as not a transient error try (Connection con = PrepUtil.getConnection("jdbc:sqlserver://" + randomServer + ";loginTimeout=" + timeout)) { fail(TestResource.getResource("R_shouldNotConnect")); } catch (Exception e) { @@ -242,7 +245,7 @@ public void testConnectRetryServerError() { int interval = defaultTimeout; // long interval so we can tell if there was a retry long timeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval - // non existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time + // non-existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time try (Connection con = PrepUtil.getConnection( TestUtils.addOrOverrideProperty(connectionString, "database", RandomUtil.getIdentifier("database")) + ";loginTimeout=" + timeout + ";connectRetryCount=" + 1 + ";connectRetryInterval=" + interval @@ -280,10 +283,10 @@ public void testConnectRetryServerErrorDS() { int interval = defaultTimeout; // long interval so we can tell if there was a retry long loginTimeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval - // non existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time + // non-existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time SQLServerDataSource ds = new SQLServerDataSource(); String connectStr = TestUtils.addOrOverrideProperty(connectionString, "database", - RandomUtil.getIdentifier("database")) + ";logintimeout=" + loginTimeout + ";connectRetryCount=1" + RandomUtil.getIdentifier("database")) + ";loginTimeout=" + loginTimeout + ";connectRetryCount=1" + ";connectRetryInterval=" + interval; updateDataSource(connectStr, ds); @@ -316,7 +319,7 @@ public void testConnectRetryTimeout() { int interval = defaultTimeout; // long interval so we can tell if there was a retry int loginTimeout = 2; - // non existent database with very short loginTimeout so there is no time to do any retry + // non-existent database with very short loginTimeout so there is no time to do any retry try (Connection con = PrepUtil.getConnection( TestUtils.addOrOverrideProperty(connectionString, "database", RandomUtil.getIdentifier("database")) + "connectRetryCount=" + (new Random().nextInt(256)) + ";connectRetryInterval=" + interval @@ -358,23 +361,23 @@ public void testSocketTimeoutBoundedByLoginTimeoutReset() throws Exception { public void testAzureEndpointRetry() { try (Connection con = PrepUtil.getConnection(connectionString)) { - Field fields[] = con.getClass().getSuperclass().getDeclaredFields(); + Field[] fields = con.getClass().getSuperclass().getDeclaredFields(); for (Field f : fields) { if (f.getName().equals("connectRetryCount")) { f.setAccessible(true); int retryCount = f.getInt(con); if (TestUtils.isAzureSynapseOnDemand(con)) { - assertTrue(retryCount == 5); // AZURE_SYNAPSE_ONDEMAND_ENDPOINT_RETRY_COUNT_DEFAULT + assertEquals(5, retryCount); // AZURE_SYNAPSE_ON_DEMAND_ENDPOINT_RETRY_COUNT_DEFAULT } else if (TestUtils.isAzure(con)) { - assertTrue(retryCount == 2); // AZURE_SERVER_ENDPOINT_RETRY_COUNT_DEFAFULT + assertEquals(2, retryCount); // AZURE_SERVER_ENDPOINT_RETRY_COUNT_DEFAULT } else { // default retryCount is 1 if not set in connection string String retryCountFromConnStr = TestUtils.getProperty(connectionString, "connectRetryCount"); int expectedRetryCount = (retryCountFromConnStr != null) ? Integer .parseInt(retryCountFromConnStr) : 1; - assertTrue(retryCount == expectedRetryCount); // default connectRetryCount + assertEquals(retryCount, expectedRetryCount); // default connectRetryCount } } } @@ -387,13 +390,14 @@ public void testAzureEndpointRetry() { * When query timeout occurs, the connection is still usable. * * @throws Exception + * when an exception occurs */ @Test @Tag(Constants.xAzureSQLDW) public void testQueryTimeout() throws Exception { try (Connection conn = getConnection()) { dropWaitForDelayProcedure(conn); - createWaitForDelayPreocedure(conn); + createWaitForDelayProcedure(conn); } try (Connection conn = PrepUtil.getConnection( @@ -424,13 +428,14 @@ public void testQueryTimeout() throws Exception { * Tests sanity of connection property. * * @throws Exception + * when an exception occurs */ @Test @Tag(Constants.xAzureSQLDW) public void testCancelQueryTimeout() throws Exception { try (Connection conn = getConnection()) { dropWaitForDelayProcedure(conn); - createWaitForDelayPreocedure(conn); + createWaitForDelayProcedure(conn); } try (Connection conn = PrepUtil.getConnection(connectionString + ";queryTimeout=" + (waitForDelaySeconds / 2) @@ -461,13 +466,14 @@ public void testCancelQueryTimeout() throws Exception { * Tests sanity of connection property. * * @throws Exception + * when an exception occurs */ @Test @Tag(Constants.xAzureSQLDW) public void testCancelQueryTimeoutOnStatement() throws Exception { try (Connection conn = getConnection()) { dropWaitForDelayProcedure(conn); - createWaitForDelayPreocedure(conn); + createWaitForDelayProcedure(conn); } try (Connection conn = PrepUtil.getConnection(connectionString + Constants.SEMI_COLON)) { @@ -499,13 +505,14 @@ public void testCancelQueryTimeoutOnStatement() throws Exception { * When socketTimeout occurs, the connection will be marked as closed. * * @throws Exception + * when an exception occurs */ @Test @Tag(Constants.xAzureSQLDW) public void testSocketTimeout() throws Exception { try (Connection conn = getConnection()) { dropWaitForDelayProcedure(conn); - createWaitForDelayPreocedure(conn); + createWaitForDelayProcedure(conn); } try (Connection conn = PrepUtil.getConnection( @@ -533,7 +540,7 @@ public void testSocketTimeout() throws Exception { } } - private static void dropWaitForDelayProcedure(Connection conn) throws SQLException { + private static void dropWaitForDelayProcedure(Connection conn) { try (Statement stmt = conn.createStatement()) { TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(waitForDelaySPName), stmt); } catch (Exception e) { @@ -541,7 +548,7 @@ private static void dropWaitForDelayProcedure(Connection conn) throws SQLExcepti } } - private void createWaitForDelayPreocedure(Connection conn) throws SQLException { + private void createWaitForDelayProcedure(Connection conn) { try (Statement stmt = conn.createStatement()) { String sql = "CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(waitForDelaySPName) + " AS" + " BEGIN" + " WAITFOR DELAY '00:00:" + waitForDelaySeconds + "';" + " END"; @@ -551,16 +558,6 @@ private void createWaitForDelayPreocedure(Connection conn) throws SQLException { } } - static Field[] getConnectionFields(Connection c) { - Class cls = c.getClass(); - // SQLServerConnection43 is returned for Java >=9 so need to get super class - if (cls.getName() == "com.microsoft.sqlserver.jdbc.SQLServerConnection43") { - return cls.getSuperclass().getDeclaredFields(); - } - - return cls.getDeclaredFields(); - } - @AfterAll public static void cleanup() throws SQLException { try (Connection conn = getConnection()) { From 16397df96b40e70cbe6834f2d6f6f54e6f40cafb Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 18 Sep 2024 12:56:07 -0700 Subject: [PATCH 04/30] Fix test --- .../com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index 892d27de1..a9f22ae85 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -470,8 +470,7 @@ public void testConnectCountInLoginAndCorrectRetryCountForMultipleValues() { * Tests whether connectRetryCount and connectRetryInterval are properly respected in the login loop. As well, tests * that connection is retried the proper number of times. */ - @Test - public void testConnectCountInLoginAndCorrectRetryCount(int connectRetryCount) { + private void testConnectCountInLoginAndCorrectRetryCount(int connectRetryCount) { long timerStart = 0; int connectRetryInterval = 60; From e8b93e56f5d95e55943cee2f685dc936dfa980ed Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 18 Sep 2024 14:34:01 -0700 Subject: [PATCH 05/30] Testing --- .../com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index a9f22ae85..05c44b600 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -502,6 +502,8 @@ private void testConnectCountInLoginAndCorrectRetryCount(int connectRetryCount) assertTrue(totalTime < (longLoginTimeout * 1000L), TestResource.getResource("R_executionTooLong")); // We should at least take as long as the retry interval between all retries past the first. int minTimeInSecs = connectRetryInterval * (connectRetryCount - 1); + System.out.println("totalTime: " + totalTime); + System.out.println("minTimeInSecs: " + minTimeInSecs); assertTrue(totalTime > (minTimeInSecs * 1000L), TestResource.getResource("R_executionNotLong")); } } From aec6b52c45c2a6a81835c1e9e35d1646efbfc38b Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 18 Sep 2024 15:21:50 -0700 Subject: [PATCH 06/30] ok.. --- .../java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 99d80d526..585b0593b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -2028,6 +2028,7 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio } if (connectRetryAttempt > 1) { // We do not sleep for first retry; first retry is immediate + System.out.println("Sleeping for: " + TimeUnit.SECONDS.toMillis(connectRetryInterval) + " ms"); sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); } } From 825a284f46ec29ba2b5f0f203a0bcaded718ae7b Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 18 Sep 2024 16:19:39 -0700 Subject: [PATCH 07/30] Grasping at straws --- .../com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 585b0593b..79dfc4526 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -1955,6 +1955,8 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio long start = System.currentTimeMillis(); for (int connectRetryAttempt = 0, tlsRetryAttempt = 0;;) { try { + System.out.println("---------------------Entered main loop---------------------"); + System.out.println("(1) connectRetryCount=" + connectRetryCount); if (0 == elapsedSeconds || elapsedSeconds < loginTimeoutSeconds) { if (0 < tlsRetryAttempt && INTERMITTENT_TLS_MAX_RETRY > tlsRetryAttempt) { if (connectionlogger.isLoggable(Level.FINE)) { @@ -1971,6 +1973,7 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio return connectInternal(propsIn, pooledConnection); } } catch (SQLServerException e) { + System.out.println("---------------------Caught Exception---------------------"); elapsedSeconds = ((System.currentTimeMillis() - start) / 1000L); // special case for TLS intermittent failures: no wait retries @@ -1983,6 +1986,7 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio } tlsRetryAttempt++; } else { + System.out.println("---------------------Entered else(1)---------------------"); // TLS max retry exceeded if (tlsRetryAttempt > INTERMITTENT_TLS_MAX_RETRY) { if (connectionlogger.isLoggable(Level.FINE)) { @@ -1990,6 +1994,7 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio + INTERMITTENT_TLS_MAX_RETRY + ") reached. "); } } + System.out.println("(2) connectRetryCount=" + connectRetryCount); if (0 == connectRetryCount) { // connection retry disabled @@ -2002,6 +2007,7 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio } throw e; } else { + System.out.println("---------------------Entered else(2)---------------------"); // only retry if transient error SQLServerError sqlServerError = e.getSQLServerError(); if (!TransientError.isTransientError(sqlServerError)) { From 4e61b548b1b4eaa0279ea011aa99a324e0016c8b Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 18 Sep 2024 18:01:55 -0700 Subject: [PATCH 08/30] more debugs, yum --- .../sqlserver/jdbc/SQLServerConnection.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 79dfc4526..6eb65f04a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -1956,7 +1956,7 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio for (int connectRetryAttempt = 0, tlsRetryAttempt = 0;;) { try { System.out.println("---------------------Entered main loop---------------------"); - System.out.println("(1) connectRetryCount=" + connectRetryCount); + System.out.println("\t(1) connectRetryCount=" + connectRetryCount); if (0 == elapsedSeconds || elapsedSeconds < loginTimeoutSeconds) { if (0 < tlsRetryAttempt && INTERMITTENT_TLS_MAX_RETRY > tlsRetryAttempt) { if (connectionlogger.isLoggable(Level.FINE)) { @@ -1994,7 +1994,8 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio + INTERMITTENT_TLS_MAX_RETRY + ") reached. "); } } - System.out.println("(2) connectRetryCount=" + connectRetryCount); + System.out.println("\t(2) connectRetryCount=" + connectRetryCount); + System.out.println("\tconnectRetryAttempt: " + connectRetryAttempt); if (0 == connectRetryCount) { // connection retry disabled @@ -2011,6 +2012,8 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio // only retry if transient error SQLServerError sqlServerError = e.getSQLServerError(); if (!TransientError.isTransientError(sqlServerError)) { + System.out.println("----------------------THROW ON NON-TRANSIENT ERROR--------------------"); + System.out.println(e.getSQLServerError()); throw e; } @@ -2023,6 +2026,9 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio + connectRetryInterval + ")s >= loginTimeout(" + loginTimeoutSeconds + ")s"); } + System.out.println("----------------------THROW ON NO TIME TO RETRY--------------------"); + System.out.println("(elapsedSeconds + connectRetryInterval): " + (elapsedSeconds + connectRetryInterval)); + System.out.println("loginTimeout: " + loginTimeoutSeconds); throw e; } @@ -2034,10 +2040,12 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio } if (connectRetryAttempt > 1) { // We do not sleep for first retry; first retry is immediate - System.out.println("Sleeping for: " + TimeUnit.SECONDS.toMillis(connectRetryInterval) + " ms"); + System.out.println("\tSleeping for: " + TimeUnit.SECONDS.toMillis(connectRetryInterval) + " ms"); sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); } + System.out.println("\tEND OF ELSE (2)"); } + System.out.println("\tEND OF ELSE (1)"); } } } From 8178088263efb8e96d11c7506ea7d88d448d72c4 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 18 Sep 2024 18:25:56 -0700 Subject: [PATCH 09/30] focus on transient error --- .../microsoft/sqlserver/jdbc/SQLServerConnection.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 6eb65f04a..5f9ea05b3 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -2009,11 +2009,17 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio throw e; } else { System.out.println("---------------------Entered else(2)---------------------"); + + System.out.println("--------------------Error info--------------------------"); + System.out.println(e.getSQLServerError()); + System.out.println(e.getMessage()); + System.out.println(e.getDriverErrorCode()); + System.out.println(e.toString()); + // only retry if transient error SQLServerError sqlServerError = e.getSQLServerError(); if (!TransientError.isTransientError(sqlServerError)) { - System.out.println("----------------------THROW ON NON-TRANSIENT ERROR--------------------"); - System.out.println(e.getSQLServerError()); + System.out.println("----------------------ABOVE IS NON-TRANSIENT ERROR--------------------"); throw e; } From 97c49d3dc2e0f0e534a94b20c001c0862f96086c Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 18 Sep 2024 18:50:06 -0700 Subject: [PATCH 10/30] Fix for azure --- .../sqlserver/jdbc/SQLServerConnectionTest.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index 05c44b600..69cc76a3c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -500,11 +500,17 @@ private void testConnectCountInLoginAndCorrectRetryCount(int connectRetryCount) // Maximum is unknown, but is needs to be less than longLoginTimeout or else this is an issue. assertTrue(totalTime < (longLoginTimeout * 1000L), TestResource.getResource("R_executionTooLong")); - // We should at least take as long as the retry interval between all retries past the first. + int minTimeInSecs = connectRetryInterval * (connectRetryCount - 1); System.out.println("totalTime: " + totalTime); System.out.println("minTimeInSecs: " + minTimeInSecs); - assertTrue(totalTime > (minTimeInSecs * 1000L), TestResource.getResource("R_executionNotLong")); + // We should at least take as long as the retry interval between all retries past the first. + // Only measure minimum if error is R_cannotOpenDatabase, as that is guaranteed to follow retry interval sleeps + if (e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase"))) { + + assertTrue(totalTime > (minTimeInSecs * 1000L), TestResource.getResource("R_executionNotLong")); + } + } } From b5c01ad0c74c44c4919aab9cc54864bdcb066b47 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 18 Sep 2024 19:19:42 -0700 Subject: [PATCH 11/30] Cleanup --- .../sqlserver/jdbc/SQLServerConnection.java | 20 ------- .../jdbc/SQLServerConnectionTest.java | 17 +++--- .../sqlserver/jdbc/TestResource.java | 10 ++-- .../jdbc/connection/TimeoutTest.java | 56 ++++++++++--------- 4 files changed, 43 insertions(+), 60 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 5f9ea05b3..73d38bf57 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -1955,8 +1955,6 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio long start = System.currentTimeMillis(); for (int connectRetryAttempt = 0, tlsRetryAttempt = 0;;) { try { - System.out.println("---------------------Entered main loop---------------------"); - System.out.println("\t(1) connectRetryCount=" + connectRetryCount); if (0 == elapsedSeconds || elapsedSeconds < loginTimeoutSeconds) { if (0 < tlsRetryAttempt && INTERMITTENT_TLS_MAX_RETRY > tlsRetryAttempt) { if (connectionlogger.isLoggable(Level.FINE)) { @@ -1973,7 +1971,6 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio return connectInternal(propsIn, pooledConnection); } } catch (SQLServerException e) { - System.out.println("---------------------Caught Exception---------------------"); elapsedSeconds = ((System.currentTimeMillis() - start) / 1000L); // special case for TLS intermittent failures: no wait retries @@ -1986,7 +1983,6 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio } tlsRetryAttempt++; } else { - System.out.println("---------------------Entered else(1)---------------------"); // TLS max retry exceeded if (tlsRetryAttempt > INTERMITTENT_TLS_MAX_RETRY) { if (connectionlogger.isLoggable(Level.FINE)) { @@ -1994,8 +1990,6 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio + INTERMITTENT_TLS_MAX_RETRY + ") reached. "); } } - System.out.println("\t(2) connectRetryCount=" + connectRetryCount); - System.out.println("\tconnectRetryAttempt: " + connectRetryAttempt); if (0 == connectRetryCount) { // connection retry disabled @@ -2008,18 +2002,10 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio } throw e; } else { - System.out.println("---------------------Entered else(2)---------------------"); - - System.out.println("--------------------Error info--------------------------"); - System.out.println(e.getSQLServerError()); - System.out.println(e.getMessage()); - System.out.println(e.getDriverErrorCode()); - System.out.println(e.toString()); // only retry if transient error SQLServerError sqlServerError = e.getSQLServerError(); if (!TransientError.isTransientError(sqlServerError)) { - System.out.println("----------------------ABOVE IS NON-TRANSIENT ERROR--------------------"); throw e; } @@ -2032,9 +2018,6 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio + connectRetryInterval + ")s >= loginTimeout(" + loginTimeoutSeconds + ")s"); } - System.out.println("----------------------THROW ON NO TIME TO RETRY--------------------"); - System.out.println("(elapsedSeconds + connectRetryInterval): " + (elapsedSeconds + connectRetryInterval)); - System.out.println("loginTimeout: " + loginTimeoutSeconds); throw e; } @@ -2046,12 +2029,9 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio } if (connectRetryAttempt > 1) { // We do not sleep for first retry; first retry is immediate - System.out.println("\tSleeping for: " + TimeUnit.SECONDS.toMillis(connectRetryInterval) + " ms"); sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); } - System.out.println("\tEND OF ELSE (2)"); } - System.out.println("\tEND OF ELSE (1)"); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index 69cc76a3c..2cb984be0 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -175,7 +175,7 @@ public void testDataSource() throws SQLServerException { ds.setTrustStorePassword(stringPropValue); assertEquals(stringPropValue, ds.getTrustStorePassword(), TestResource.getResource("R_valuesAreDifferent")); - // verify encrypt=true options + // verify enrypt=true options ds.setEncrypt(EncryptOption.MANDATORY.toString()); assertEquals("True", EncryptOption.valueOfString(ds.getEncrypt()).toString(), TestResource.getResource("R_valuesAreDifferent")); @@ -494,20 +494,19 @@ private void testConnectCountInLoginAndCorrectRetryCount(int connectRetryCount) || (TestUtils.getProperty(connectionString, "msiClientId") != null && (e.getMessage() .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase()) || e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_MINotAvailable").toLowerCase()))), + .contains(TestResource.getResource("R_MInotAvailable").toLowerCase()))), e.getMessage()); long totalTime = System.currentTimeMillis() - timerStart; // Maximum is unknown, but is needs to be less than longLoginTimeout or else this is an issue. assertTrue(totalTime < (longLoginTimeout * 1000L), TestResource.getResource("R_executionTooLong")); - int minTimeInSecs = connectRetryInterval * (connectRetryCount - 1); - System.out.println("totalTime: " + totalTime); - System.out.println("minTimeInSecs: " + minTimeInSecs); // We should at least take as long as the retry interval between all retries past the first. - // Only measure minimum if error is R_cannotOpenDatabase, as that is guaranteed to follow retry interval sleeps + // Of the above acceptable errors (R_cannotOpenDatabase, R_loginFailedMI, R_MInotAvailable), only + // R_cannotOpenDatabase is transient, and can be used to measure multiple retries with retry interval. The + // others will exit before they have a chance to wait, and min will be too low. if (e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase"))) { - + int minTimeInSecs = connectRetryInterval * (connectRetryCount - 1); assertTrue(totalTime > (minTimeInSecs * 1000L), TestResource.getResource("R_executionNotLong")); } @@ -784,7 +783,7 @@ public void testClientConnectionId() throws Exception { // Non-existent host, ClientConnectionId should not be available in error message try (Connection conn = PrepUtil.getConnection( - connectionString + ";instanceName=" + RandomUtil.getIdentifier("Instance") + ";loginTimeout=5;")) { + connectionString + ";instanceName=" + RandomUtil.getIdentifier("Instance") + ";logintimeout=5;")) { conn.close(); } catch (SQLException e) { @@ -1301,7 +1300,7 @@ public void testServerNameField() throws SQLException { String[] serverNameAndPort = connectionString.substring(subProtocol.length(), indexOfFirstDelimiter).split(":"); String connectionProperties = connectionString.substring(indexOfFirstDelimiter, indexOfLastDelimiter + 1); - String loginTimeout = "loginTimeout=15"; + String loginTimeout = "loginTimout=15"; // Server name field is empty but serverName connection property is set, should pass String emptyServerNameField = subProtocol + connectionProperties + "serverName=" + serverNameAndPort[0] + ";"; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index c20f63d7a..29a5e21dd 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -30,7 +30,7 @@ protected Object[][] getContents() { // the keys must be prefixed with R_ to denote they are resource strings and their names should follow the // camelCasing // convention and be descriptive - static final Object[][] contents = {{"R_wrongEnv", "Aborting test: As this is not the right environment: "}, + static final Object[][] contents = {{"R_wrongEnv", "Aborting test: As this is not the right environement: "}, {"R_fipsPropertyNotSet", "Aborting test case as FIPS_ENV property is not set."}, {"R_invalidTrustCert", "Invalid TrustServerCertificate value."}, {"R_invalidEncrypt", "Invalid encrypt value."}, {"R_notImplemented", "not implemented"}, @@ -42,11 +42,9 @@ protected Object[][] getContents() { {"R_givenValueType", "The given value of type"}, {"R_lengthTruncated", " The inserted length is truncated or not correct!"}, {"R_timeValueTruncated", " The time value is truncated or not correct!"}, - {"R_nullPointerExceptionFromResultSet", - "Cannot invoke \"java.sql.ResultSet.next()\" because \"rs\" is null"}, + {"R_nullPointerExceptionFromResultSet", "Cannot invoke \"java.sql.ResultSet.next()\" because \"rs\" is null"}, {"R_invalidErrorMessage", "Invalid Error Message: "}, - {"R_kerberosNativeGSSFailure", - "No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)"}, + {"R_kerberosNativeGSSFailure", "No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)"}, {"R_expectedFailPassed", "Expected failure did not fail"}, {"R_dataTypeNotFound", "Cannot find data type"}, {"R_illegalCharWktPosition", "Illegal character in Well-Known text at position {0}."}, {"R_illegalCharWkt", "Illegal Well-Known text. Please make sure Well-Known text is valid."}, @@ -218,5 +216,5 @@ protected Object[][] getContents() { {"R_expectedClassDoesNotMatchActualClass", "Expected column class {0} does not match actual column class {1} for column {2}."}, {"R_loginFailedMI", "Login failed for user ''"}, - {"R_MINotAvailable", "Managed Identity authentication is not available"},}; + {"R_MInotAvailable", "Managed Identity authentication is not available"},}; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java index 1e8fdff83..0007123bb 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java @@ -62,7 +62,7 @@ public void testDefaultLoginTimeout() { long totalTime = 0; long timerStart = System.currentTimeMillis(); - // non-existing server and default values to see if took default timeout + // non existing server and default values to see if took default timeout try (Connection con = PrepUtil.getConnection("jdbc:sqlserver://" + randomServer)) { fail(TestResource.getResource("R_shouldNotConnect")); } catch (Exception e) { @@ -80,7 +80,7 @@ public void testDefaultLoginTimeout() { // time should be < default loginTimeout assertTrue(totalTime < TimeUnit.SECONDS.toMillis(defaultTimeout), - "total time: " + totalTime + " default loginTimeout: " + TimeUnit.SECONDS.toMillis(defaultTimeout)); + "total time: " + totalTime + " default logintimeout: " + TimeUnit.SECONDS.toMillis(defaultTimeout)); } // test setting loginTimeout value @@ -91,8 +91,8 @@ public void testURLLoginTimeout() { long timerStart = System.currentTimeMillis(); - // non-existing server and set loginTimeout - try (Connection con = PrepUtil.getConnection("jdbc:sqlserver://" + randomServer + ";loginTimeout=" + timeout)) { + // non existing server and set loginTimeout + try (Connection con = PrepUtil.getConnection("jdbc:sqlserver://" + randomServer + ";loginTimout=" + timeout)) { fail(TestResource.getResource("R_shouldNotConnect")); } catch (Exception e) { totalTime = System.currentTimeMillis() - timerStart; @@ -183,7 +183,7 @@ public void testConnectRetryDisable() { int interval = defaultTimeout; // long interval so we can tell if there was a retry long timeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval - // non-existent server with long loginTimeout, should return fast if no retries at all + // non existent server with long loginTimeout, should return fast if no retries at all try (Connection con = PrepUtil.getConnection( "jdbc:sqlserver://" + randomServer + ";transparentNetworkIPResolution=false;loginTimeout=" + timeout + ";connectRetryCount=0;connectInterval=" + interval)) { @@ -205,14 +205,14 @@ public void testConnectRetryDisable() { "total time: " + totalTime + " interval: " + TimeUnit.SECONDS.toMillis(interval)); } - // Test connect retry for non-existent server with loginTimeout + // Test connect retry for non existent server with loginTimeout @Test public void testConnectRetryBadServer() { long totalTime = 0; long timerStart = System.currentTimeMillis(); int timeout = 15; - // non-existent server with very short loginTimeout, no retry will happen as not a transient error + // non existent server with very short loginTimeout, no retry will happen as not a transient error try (Connection con = PrepUtil.getConnection("jdbc:sqlserver://" + randomServer + ";loginTimeout=" + timeout)) { fail(TestResource.getResource("R_shouldNotConnect")); } catch (Exception e) { @@ -245,7 +245,7 @@ public void testConnectRetryServerError() { int interval = defaultTimeout; // long interval so we can tell if there was a retry long timeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval - // non-existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time + // non existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time try (Connection con = PrepUtil.getConnection( TestUtils.addOrOverrideProperty(connectionString, "database", RandomUtil.getIdentifier("database")) + ";loginTimeout=" + timeout + ";connectRetryCount=" + 1 + ";connectRetryInterval=" + interval @@ -283,10 +283,10 @@ public void testConnectRetryServerErrorDS() { int interval = defaultTimeout; // long interval so we can tell if there was a retry long loginTimeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval - // non-existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time + // non existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time SQLServerDataSource ds = new SQLServerDataSource(); String connectStr = TestUtils.addOrOverrideProperty(connectionString, "database", - RandomUtil.getIdentifier("database")) + ";loginTimeout=" + loginTimeout + ";connectRetryCount=1" + RandomUtil.getIdentifier("database")) + ";logintimeout=" + loginTimeout + ";connectRetryCount=1" + ";connectRetryInterval=" + interval; updateDataSource(connectStr, ds); @@ -319,7 +319,7 @@ public void testConnectRetryTimeout() { int interval = defaultTimeout; // long interval so we can tell if there was a retry int loginTimeout = 2; - // non-existent database with very short loginTimeout so there is no time to do any retry + // non existent database with very short loginTimeout so there is no time to do any retry try (Connection con = PrepUtil.getConnection( TestUtils.addOrOverrideProperty(connectionString, "database", RandomUtil.getIdentifier("database")) + "connectRetryCount=" + (new Random().nextInt(256)) + ";connectRetryInterval=" + interval @@ -361,23 +361,23 @@ public void testSocketTimeoutBoundedByLoginTimeoutReset() throws Exception { public void testAzureEndpointRetry() { try (Connection con = PrepUtil.getConnection(connectionString)) { - Field[] fields = con.getClass().getSuperclass().getDeclaredFields(); + Field fields[] = con.getClass().getSuperclass().getDeclaredFields(); for (Field f : fields) { if (f.getName().equals("connectRetryCount")) { f.setAccessible(true); int retryCount = f.getInt(con); if (TestUtils.isAzureSynapseOnDemand(con)) { - assertEquals(5, retryCount); // AZURE_SYNAPSE_ON_DEMAND_ENDPOINT_RETRY_COUNT_DEFAULT + assertTrue(retryCount == 5); // AZURE_SYNAPSE_ONDEMAND_ENDPOINT_RETRY_COUNT_DEFAULT } else if (TestUtils.isAzure(con)) { - assertEquals(2, retryCount); // AZURE_SERVER_ENDPOINT_RETRY_COUNT_DEFAULT + assertTrue(retryCount == 2); // AZURE_SERVER_ENDPOINT_RETRY_COUNT_DEFAFULT } else { // default retryCount is 1 if not set in connection string String retryCountFromConnStr = TestUtils.getProperty(connectionString, "connectRetryCount"); int expectedRetryCount = (retryCountFromConnStr != null) ? Integer .parseInt(retryCountFromConnStr) : 1; - assertEquals(retryCount, expectedRetryCount); // default connectRetryCount + assertTrue(retryCount == expectedRetryCount); // default connectRetryCount } } } @@ -390,14 +390,13 @@ public void testAzureEndpointRetry() { * When query timeout occurs, the connection is still usable. * * @throws Exception - * when an exception occurs */ @Test @Tag(Constants.xAzureSQLDW) public void testQueryTimeout() throws Exception { try (Connection conn = getConnection()) { dropWaitForDelayProcedure(conn); - createWaitForDelayProcedure(conn); + createWaitForDelayPreocedure(conn); } try (Connection conn = PrepUtil.getConnection( @@ -428,14 +427,13 @@ public void testQueryTimeout() throws Exception { * Tests sanity of connection property. * * @throws Exception - * when an exception occurs */ @Test @Tag(Constants.xAzureSQLDW) public void testCancelQueryTimeout() throws Exception { try (Connection conn = getConnection()) { dropWaitForDelayProcedure(conn); - createWaitForDelayProcedure(conn); + createWaitForDelayPreocedure(conn); } try (Connection conn = PrepUtil.getConnection(connectionString + ";queryTimeout=" + (waitForDelaySeconds / 2) @@ -466,14 +464,13 @@ public void testCancelQueryTimeout() throws Exception { * Tests sanity of connection property. * * @throws Exception - * when an exception occurs */ @Test @Tag(Constants.xAzureSQLDW) public void testCancelQueryTimeoutOnStatement() throws Exception { try (Connection conn = getConnection()) { dropWaitForDelayProcedure(conn); - createWaitForDelayProcedure(conn); + createWaitForDelayPreocedure(conn); } try (Connection conn = PrepUtil.getConnection(connectionString + Constants.SEMI_COLON)) { @@ -505,14 +502,13 @@ public void testCancelQueryTimeoutOnStatement() throws Exception { * When socketTimeout occurs, the connection will be marked as closed. * * @throws Exception - * when an exception occurs */ @Test @Tag(Constants.xAzureSQLDW) public void testSocketTimeout() throws Exception { try (Connection conn = getConnection()) { dropWaitForDelayProcedure(conn); - createWaitForDelayProcedure(conn); + createWaitForDelayPreocedure(conn); } try (Connection conn = PrepUtil.getConnection( @@ -540,7 +536,7 @@ public void testSocketTimeout() throws Exception { } } - private static void dropWaitForDelayProcedure(Connection conn) { + private static void dropWaitForDelayProcedure(Connection conn) throws SQLException { try (Statement stmt = conn.createStatement()) { TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(waitForDelaySPName), stmt); } catch (Exception e) { @@ -548,7 +544,7 @@ private static void dropWaitForDelayProcedure(Connection conn) { } } - private void createWaitForDelayProcedure(Connection conn) { + private void createWaitForDelayPreocedure(Connection conn) throws SQLException { try (Statement stmt = conn.createStatement()) { String sql = "CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(waitForDelaySPName) + " AS" + " BEGIN" + " WAITFOR DELAY '00:00:" + waitForDelaySeconds + "';" + " END"; @@ -558,6 +554,16 @@ private void createWaitForDelayProcedure(Connection conn) { } } + static Field[] getConnectionFields(Connection c) { + Class cls = c.getClass(); + // SQLServerConnection43 is returned for Java >=9 so need to get super class + if (cls.getName() == "com.microsoft.sqlserver.jdbc.SQLServerConnection43") { + return cls.getSuperclass().getDeclaredFields(); + } + + return cls.getDeclaredFields(); + } + @AfterAll public static void cleanup() throws SQLException { try (Connection conn = getConnection()) { From f8b33e18a29c48ef52e8a85a9c408663cf5a1aff Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 18 Sep 2024 19:23:54 -0700 Subject: [PATCH 12/30] More cleanup --- .../sqlserver/jdbc/SQLServerConnection.java | 1 - .../sqlserver/jdbc/SQLServerConnectionTest.java | 7 +++---- .../sqlserver/jdbc/connection/TimeoutTest.java | 13 +++++-------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 73d38bf57..99d80d526 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -2002,7 +2002,6 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio } throw e; } else { - // only retry if transient error SQLServerError sqlServerError = e.getSQLServerError(); if (!TransientError.isTransientError(sqlServerError)) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index 2cb984be0..4061035a7 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -5,10 +5,9 @@ package com.microsoft.sqlserver.jdbc; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.io.Reader; @@ -194,7 +193,7 @@ public void testDataSource() throws SQLServerException { assertEquals("False", EncryptOption.valueOfString(ds.getEncrypt()).toString(), TestResource.getResource("R_valuesAreDifferent")); - // verify encrypt=strict options + // verify enrypt=strict options ds.setEncrypt(EncryptOption.STRICT.toString()); assertEquals("Strict", EncryptOption.valueOfString(ds.getEncrypt()).toString(), TestResource.getResource("R_valuesAreDifferent")); @@ -486,7 +485,7 @@ private void testConnectCountInLoginAndCorrectRetryCount(int connectRetryCount) timerStart = System.currentTimeMillis(); try (Connection con = ds.getConnection()) { - assertNull(con, TestResource.getResource("R_shouldNotConnect")); + assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); } } catch (Exception e) { assertTrue( diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java index 0007123bb..d7290b262 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java @@ -49,11 +49,8 @@ public static void setupTests() throws Exception { /* * TODO: * The tests below uses a simple interval counting logic to determine whether there was at least 1 retry. - * Given the interval is long enough, then 1 retry should take at least 1 interval long, so if it took < 1 interval, - * then it assumes there were no retry. However, this only works if TNIR or failover is not enabled since those cases - * should retry but no wait interval in between. So this interval counting can not detect these cases. - * Note a better and more reliable way would be to check attemptNumber using reflection to - * determine the number of retries. + * Given the interval is long enough, then 1 retry should take at least 1 interval long, so if it took < 1 interval, then it assumes there were no retry. However, this only works if TNIR or failover is not enabled since those cases should retry but no wait interval in between. So this interval counting can not detect these cases. + * Note a better and more reliable way would be to check attemptNumber using reflection to determine the number of retries. */ // test default loginTimeout used if not specified in connection string @@ -80,7 +77,7 @@ public void testDefaultLoginTimeout() { // time should be < default loginTimeout assertTrue(totalTime < TimeUnit.SECONDS.toMillis(defaultTimeout), - "total time: " + totalTime + " default logintimeout: " + TimeUnit.SECONDS.toMillis(defaultTimeout)); + "total time: " + totalTime + " default loginTimout: " + TimeUnit.SECONDS.toMillis(defaultTimeout)); } // test setting loginTimeout value @@ -92,7 +89,7 @@ public void testURLLoginTimeout() { long timerStart = System.currentTimeMillis(); // non existing server and set loginTimeout - try (Connection con = PrepUtil.getConnection("jdbc:sqlserver://" + randomServer + ";loginTimout=" + timeout)) { + try (Connection con = PrepUtil.getConnection("jdbc:sqlserver://" + randomServer + ";logintimeout=" + timeout)) { fail(TestResource.getResource("R_shouldNotConnect")); } catch (Exception e) { totalTime = System.currentTimeMillis() - timerStart; @@ -205,7 +202,7 @@ public void testConnectRetryDisable() { "total time: " + totalTime + " interval: " + TimeUnit.SECONDS.toMillis(interval)); } - // Test connect retry for non existent server with loginTimeout + // Test connect retry for non-existent server with loginTimeout @Test public void testConnectRetryBadServer() { long totalTime = 0; From b0267eb8e4481cf0a709756aa37e13ef79d66837 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 18 Sep 2024 19:26:21 -0700 Subject: [PATCH 13/30] More cleanup --- .../com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 1 + .../com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 99d80d526..1c07626d4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -2026,6 +2026,7 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio + sqlServerError.getErrorNumber() + ". Wait for connectRetryInterval(" + connectRetryInterval + ")s before retry."); } + if (connectRetryAttempt > 1) { // We do not sleep for first retry; first retry is immediate sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index 4061035a7..d66e096c1 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -5,9 +5,9 @@ package com.microsoft.sqlserver.jdbc; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.io.Reader; @@ -174,7 +174,7 @@ public void testDataSource() throws SQLServerException { ds.setTrustStorePassword(stringPropValue); assertEquals(stringPropValue, ds.getTrustStorePassword(), TestResource.getResource("R_valuesAreDifferent")); - // verify enrypt=true options + // verify encrypt=true options ds.setEncrypt(EncryptOption.MANDATORY.toString()); assertEquals("True", EncryptOption.valueOfString(ds.getEncrypt()).toString(), TestResource.getResource("R_valuesAreDifferent")); From fae191a7db279ba821a766cc116d0cef01ea4fab Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Thu, 19 Sep 2024 11:55:13 -0700 Subject: [PATCH 14/30] Trying to resolve thread interrupt test --- .../java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 1c07626d4..e1e33747a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3545,8 +3545,7 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu + attemptNumber); } - sleepForInterval(fedauthRetryInterval); - fedauthRetryInterval = (fedauthRetryInterval < 500) ? fedauthRetryInterval * 2 : 1000; + sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); } } From 8e2d84687032864c1da13cf148ee3512e2392f46 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Thu, 19 Sep 2024 12:27:30 -0700 Subject: [PATCH 15/30] a different try --- .../microsoft/sqlserver/jdbc/SQLServerConnection.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index e1e33747a..4df24bb4c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -2027,10 +2027,9 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio + connectRetryInterval + ")s before retry."); } - if (connectRetryAttempt > 1) { - // We do not sleep for first retry; first retry is immediate - sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); - } + // We do not sleep for first retry; first retry is immediate + sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); + } } } @@ -3545,7 +3544,8 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu + attemptNumber); } - sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); + sleepForInterval(fedauthRetryInterval); + fedauthRetryInterval = (fedauthRetryInterval < 500) ? fedauthRetryInterval * 2 : 1000; } } From 0ed3eb52970945ba2372a2f3d7a87ea76c678a49 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Thu, 19 Sep 2024 13:03:34 -0700 Subject: [PATCH 16/30] add more time to interrupt --- .../com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 6 +++--- .../microsoft/sqlserver/jdbc/SQLServerConnectionTest.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 4df24bb4c..a14f2e416 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -2027,9 +2027,9 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio + connectRetryInterval + ")s before retry."); } - // We do not sleep for first retry; first retry is immediate - sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); - + if (connectRetryAttempt > 1) { + // We do not sleep for first retry; first retry is immediate + } } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index d66e096c1..f0306d943 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -1063,8 +1063,8 @@ public void run() { ds.setURL(connectionString); ds.setServerName("invalidServerName" + UUID.randomUUID()); ds.setLoginTimeout(30); - ds.setConnectRetryCount(3); - ds.setConnectRetryInterval(10); + ds.setConnectRetryCount(6); + ds.setConnectRetryInterval(20); try (Connection con = ds.getConnection()) {} catch (SQLException e) {} } }; From 04b764160f3059ee166d7a04b6e1f39a6be75b9c Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Thu, 19 Sep 2024 13:38:18 -0700 Subject: [PATCH 17/30] Removed sleep mistakenly --- .../java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index a14f2e416..1c07626d4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -2029,6 +2029,7 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio if (connectRetryAttempt > 1) { // We do not sleep for first retry; first retry is immediate + sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); } } } From 2956fdfe4141c0e2104f61b4c3c12cc5b0969f38 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Thu, 19 Sep 2024 14:48:02 -0700 Subject: [PATCH 18/30] Added more variants --- .../jdbc/SQLServerConnectionTest.java | 46 ++++++++++++++++++- .../jdbc/connection/TimeoutTest.java | 28 ++++++----- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index f0306d943..f46cb886b 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -517,7 +517,7 @@ private void testConnectCountInLoginAndCorrectRetryCount(int connectRetryCount) @Tag(Constants.xAzureSQLDW) @Tag(Constants.xAzureSQLDB) @Tag(Constants.reqExternalSetup) - public void testConnectTnir() { + public void testConnectTnirWithZeroRetry() { org.junit.Assume.assumeTrue(isWindows); // no retries but should connect to TNIR (this assumes host is defined in host file @@ -528,12 +528,30 @@ public void testConnectTnir() { } } + /** + * Test connect retry > 0 but should still connect to TNIR + */ + @Test + @Tag(Constants.xAzureSQLDW) + @Tag(Constants.xAzureSQLDB) + @Tag(Constants.reqExternalSetup) + public void testConnectTnirWithNonZeroRetry() { + org.junit.Assume.assumeTrue(isWindows); + + // no retries but should connect to TNIR (this assumes host is defined in host file + try (Connection con = PrepUtil + .getConnection(connectionString + ";transparentNetworkIPResolution=true;connectRetryCount=2;serverName=" + + tnirHost);) {} catch (Exception e) { + fail(e.getMessage()); + } + } + // Test connect retry 0 and TNIR disabled @Test @Tag(Constants.xAzureSQLDW) @Tag(Constants.xAzureSQLDB) @Tag(Constants.reqExternalSetup) - public void testConnectNoTnir() { + public void testConnectNoTnirWithZeroRetry() { org.junit.Assume.assumeTrue(isWindows); // no retries no TNIR should fail even tho host is defined in host file @@ -550,6 +568,30 @@ public void testConnectNoTnir() { } } + /** + * Test connect retry > 0 and TNIR disabled + */ + @Test + @Tag(Constants.xAzureSQLDW) + @Tag(Constants.xAzureSQLDB) + @Tag(Constants.reqExternalSetup) + public void testConnectNoTnirWithNonzeroRetry() { + org.junit.Assume.assumeTrue(isWindows); + + // no retries no TNIR should fail even tho host is defined in host file + try (Connection con = PrepUtil.getConnection(connectionString + + ";transparentNetworkIPResolution=false;connectRetryCount=2;serverName=" + tnirHost);) { + assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); + } catch (Exception e) { + assertTrue(e.getMessage().matches(TestUtils.formatErrorMsg("R_tcpipConnectionFailed")) + || ((isSqlAzure() || isSqlAzureDW()) + ? e.getMessage().contains( + TestResource.getResource("R_connectTimedOut")) + : false), + e.getMessage()); + } + } + @Test @Tag(Constants.xAzureSQLDW) @Tag(Constants.xAzureSQLDB) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java index d7290b262..ed33104e6 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java @@ -172,7 +172,9 @@ public void testDMLoginTimeoutNotApplied() { } } - // Test connect retry set to 0 (disabled) + /** + * Test connect retry set to 0 (disabled) + */ @Test public void testConnectRetryDisable() { long totalTime = 0; @@ -180,10 +182,10 @@ public void testConnectRetryDisable() { int interval = defaultTimeout; // long interval so we can tell if there was a retry long timeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval - // non existent server with long loginTimeout, should return fast if no retries at all + // non-existent server with long loginTimeout, should return fast if no retries at all try (Connection con = PrepUtil.getConnection( "jdbc:sqlserver://" + randomServer + ";transparentNetworkIPResolution=false;loginTimeout=" + timeout - + ";connectRetryCount=0;connectInterval=" + interval)) { + + ";connectRetryCount=0;connectRetryInterval=" + interval)) { fail(TestResource.getResource("R_shouldNotConnect")); } catch (Exception e) { totalTime = System.currentTimeMillis() - timerStart; @@ -230,7 +232,9 @@ public void testConnectRetryBadServer() { "total time: " + totalTime + " loginTimeout: " + TimeUnit.SECONDS.toMillis(timeout)); } - // Test connect retry for database error + /** + * Test connect retry, with one retry interval, for database error + */ @Test public void testConnectRetryServerError() { String auth = TestUtils.getProperty(connectionString, "authentication"); @@ -242,10 +246,10 @@ public void testConnectRetryServerError() { int interval = defaultTimeout; // long interval so we can tell if there was a retry long timeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval - // non existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time + // non-existent database with interval < loginTimeout this will generate a 4060 transient error and retry 2 times try (Connection con = PrepUtil.getConnection( TestUtils.addOrOverrideProperty(connectionString, "database", RandomUtil.getIdentifier("database")) - + ";loginTimeout=" + timeout + ";connectRetryCount=" + 1 + ";connectRetryInterval=" + interval + + ";loginTimeout=" + timeout + ";connectRetryCount=" + 2 + ";connectRetryInterval=" + interval + ";transparentNetworkIPResolution=false")) { fail(TestResource.getResource("R_shouldNotConnect")); } catch (Exception e) { @@ -261,14 +265,16 @@ public void testConnectRetryServerError() { e.getMessage()); } - // 1 retry should be at least 1 interval long but < 2 intervals + // 2 retries should be at least 1 interval long but < 2 intervals (no interval between initial attempt and retry 1) assertTrue(TimeUnit.SECONDS.toMillis(interval) < totalTime, "interval: " + TimeUnit.SECONDS.toMillis(interval) + " total time: " + totalTime); assertTrue(totalTime < TimeUnit.SECONDS.toMillis(2 * interval), "total time: " + totalTime + " 2 * interval: " + TimeUnit.SECONDS.toMillis(interval)); } - // Test connect retry for database error using Datasource + /** + * Test connect retry, with one retry interval, for database error using Datasource + */ @Test public void testConnectRetryServerErrorDS() { String auth = TestUtils.getProperty(connectionString, "authentication"); @@ -280,10 +286,10 @@ public void testConnectRetryServerErrorDS() { int interval = defaultTimeout; // long interval so we can tell if there was a retry long loginTimeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval - // non existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time + // non-existent database with interval < loginTimeout this will generate a 4060 transient error and retry 2 times SQLServerDataSource ds = new SQLServerDataSource(); String connectStr = TestUtils.addOrOverrideProperty(connectionString, "database", - RandomUtil.getIdentifier("database")) + ";logintimeout=" + loginTimeout + ";connectRetryCount=1" + RandomUtil.getIdentifier("database")) + ";loginTimeout=" + loginTimeout + ";connectRetryCount=2" + ";connectRetryInterval=" + interval; updateDataSource(connectStr, ds); @@ -301,7 +307,7 @@ public void testConnectRetryServerErrorDS() { totalTime = System.currentTimeMillis() - timerStart; } - // 1 retry should be at least 1 interval long but < 2 intervals + // 2 retries should be at least 1 interval long but < 2 intervals (no interval between initial attempt and retry 1) assertTrue(TimeUnit.SECONDS.toMillis(interval) < totalTime, "interval: " + TimeUnit.SECONDS.toMillis(interval) + " total time: " + totalTime); assertTrue(totalTime < TimeUnit.SECONDS.toMillis(2 * interval), From 9c2caa37cbd002bd6ed86c00d3a0b445d2c3eb73 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Thu, 19 Sep 2024 15:19:11 -0700 Subject: [PATCH 19/30] Remove unneeded tests --- .../jdbc/SQLServerConnectionTest.java | 47 ++----------------- 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index f46cb886b..e1d50dd4d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -463,6 +463,7 @@ public void testConnectCountInLoginAndCorrectRetryCountForMultipleValues() { testConnectCountInLoginAndCorrectRetryCount(0); testConnectCountInLoginAndCorrectRetryCount(1); testConnectCountInLoginAndCorrectRetryCount(2); + testConnectCountInLoginAndCorrectRetryCount(5); } /** @@ -517,7 +518,7 @@ private void testConnectCountInLoginAndCorrectRetryCount(int connectRetryCount) @Tag(Constants.xAzureSQLDW) @Tag(Constants.xAzureSQLDB) @Tag(Constants.reqExternalSetup) - public void testConnectTnirWithZeroRetry() { + public void testConnectTnir() { org.junit.Assume.assumeTrue(isWindows); // no retries but should connect to TNIR (this assumes host is defined in host file @@ -528,30 +529,12 @@ public void testConnectTnirWithZeroRetry() { } } - /** - * Test connect retry > 0 but should still connect to TNIR - */ - @Test - @Tag(Constants.xAzureSQLDW) - @Tag(Constants.xAzureSQLDB) - @Tag(Constants.reqExternalSetup) - public void testConnectTnirWithNonZeroRetry() { - org.junit.Assume.assumeTrue(isWindows); - - // no retries but should connect to TNIR (this assumes host is defined in host file - try (Connection con = PrepUtil - .getConnection(connectionString + ";transparentNetworkIPResolution=true;connectRetryCount=2;serverName=" - + tnirHost);) {} catch (Exception e) { - fail(e.getMessage()); - } - } - // Test connect retry 0 and TNIR disabled @Test @Tag(Constants.xAzureSQLDW) @Tag(Constants.xAzureSQLDB) @Tag(Constants.reqExternalSetup) - public void testConnectNoTnirWithZeroRetry() { + public void testConnectNoTnir() { org.junit.Assume.assumeTrue(isWindows); // no retries no TNIR should fail even tho host is defined in host file @@ -568,30 +551,6 @@ public void testConnectNoTnirWithZeroRetry() { } } - /** - * Test connect retry > 0 and TNIR disabled - */ - @Test - @Tag(Constants.xAzureSQLDW) - @Tag(Constants.xAzureSQLDB) - @Tag(Constants.reqExternalSetup) - public void testConnectNoTnirWithNonzeroRetry() { - org.junit.Assume.assumeTrue(isWindows); - - // no retries no TNIR should fail even tho host is defined in host file - try (Connection con = PrepUtil.getConnection(connectionString - + ";transparentNetworkIPResolution=false;connectRetryCount=2;serverName=" + tnirHost);) { - assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); - } catch (Exception e) { - assertTrue(e.getMessage().matches(TestUtils.formatErrorMsg("R_tcpipConnectionFailed")) - || ((isSqlAzure() || isSqlAzureDW()) - ? e.getMessage().contains( - TestResource.getResource("R_connectTimedOut")) - : false), - e.getMessage()); - } - } - @Test @Tag(Constants.xAzureSQLDW) @Tag(Constants.xAzureSQLDB) From ca23e4e9b1184497938718e615bed2f2264d617c Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Thu, 19 Sep 2024 16:18:51 -0700 Subject: [PATCH 20/30] Remove problem line + add fix for uncaught fail for BatchExecutionTest --- .../com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java | 1 - .../sqlserver/jdbc/unit/statement/BatchExecutionTest.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index e1d50dd4d..f0306d943 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -463,7 +463,6 @@ public void testConnectCountInLoginAndCorrectRetryCountForMultipleValues() { testConnectCountInLoginAndCorrectRetryCount(0); testConnectCountInLoginAndCorrectRetryCount(1); testConnectCountInLoginAndCorrectRetryCount(2); - testConnectCountInLoginAndCorrectRetryCount(5); } /** diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index 5a436547c..13e858856 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -218,7 +218,7 @@ public void testSqlServerBulkCopyCachingConnectionLevelMultiThreaded() throws Ex Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); long ms = 1578743412000L; - long timeOut = 30000; + long timeOut = 90000; int NUMBER_SIMULTANEOUS_INSERTS = 5; try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString From c9d02ded460ebb639a19287574fa6238505ff16f Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Thu, 19 Sep 2024 22:38:37 -0700 Subject: [PATCH 21/30] Fail fixes --- .../sqlserver/jdbc/SQLServerConnection.java | 17 ++++++++++++----- .../jdbc/resiliency/BasicConnectionTest.java | 2 +- .../jdbc/unit/statement/BatchExecutionTest.java | 8 -------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 1c07626d4..a0dbfaac6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -132,7 +132,7 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial /** * Back off interval in ms for retries */ - static final int BACKOFF_INTERVAL = 100; + static final int BACKOFF_INTERVAL = 500; /** Current limit for this particular connection. */ private Boolean enablePrepareOnFirstPreparedStatementCall = null; @@ -3528,9 +3528,11 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu // We only get here when we failed to connect, but are going to re-try // After trying to connect to both servers fails, sleep for a bit to prevent clogging - // the network with requests, then update sleep interval for next iteration (max 1 second interval) - // We have to sleep for every attempt in case of non-dbMirroring scenarios (including multisubnetfailover), - // Whereas for dbMirroring, we sleep for every two attempts as each attempt is to a different server. + // the network with requests, then update sleep interval for next iteration (to a maximum specified by + // connectRetryInterval [default 10]). We have to sleep for every attempt in case of non-dbMirroring + // scenarios (including multisubnetfailover), whereas for dbMirroring, we sleep for every two attempts + // as each attempt is to a different server. + // Make sure there's enough time to do another retry if (!isDBMirroring || (isDBMirroring && (0 == attemptNumber % 2)) && (attemptNumber < connectRetryCount && connectRetryCount != 0) && timerRemaining( @@ -3546,7 +3548,12 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu } sleepForInterval(fedauthRetryInterval); - fedauthRetryInterval = (fedauthRetryInterval < 500) ? fedauthRetryInterval * 2 : 1000; + int connectRetryIntervalInSeconds = (int) TimeUnit.SECONDS.toMillis(connectRetryInterval); + + fedauthRetryInterval *= 2; // Double each time + if (fedauthRetryInterval > connectRetryIntervalInSeconds) { + fedauthRetryInterval = connectRetryIntervalInSeconds; // To a maximum of connectRetryInterval + } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java index d4ba85fa5..d9b8e3e92 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java @@ -68,7 +68,7 @@ public void testBasicConnectionAAD() throws Exception { basicReconnect("jdbc:sqlserver://" + azureServer + ";database=" + azureDatabase + ";user=" + azureUserName + ";password=" + azurePassword - + ";loginTimeout=90;Authentication=ActiveDirectoryPassword"); + + ";loginTimeout=90;Authentication=ActiveDirectoryPassword;"); retry = THROTTLE_RETRY_COUNT + 1; } catch (Exception e) { if (e.getMessage().matches(TestUtils.formatErrorMsg("R_crClientAllRecoveryAttemptsFailed"))) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index 13e858856..bac0ab18c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -243,14 +243,6 @@ public void testSqlServerBulkCopyCachingConnectionLevelMultiThreaded() throws Ex ((HashMap) bulkcopyCache).clear(); - TimerTask task = new TimerTask() { - public void run() { - ((HashMap) bulkcopyCache).clear(); - fail(TestResource.getResource("R_executionTooLong")); - } - }; - Timer timer = new Timer("Timer"); - timer.schedule(task, timeOut); // Run a timer to help us exit if we get deadlocked final CountDownLatch countDownLatch = new CountDownLatch(NUMBER_SIMULTANEOUS_INSERTS); Runnable runnable = () -> { From 88e9e40ac68cddc0358c3a3264bd2cfaf726f8fb Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Fri, 20 Sep 2024 00:21:59 -0700 Subject: [PATCH 22/30] Added back the timer to the BatchExecutionTest; adjusted timeout time --- .../jdbc/unit/statement/BatchExecutionTest.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index bac0ab18c..4f0b3917d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -218,7 +218,7 @@ public void testSqlServerBulkCopyCachingConnectionLevelMultiThreaded() throws Ex Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); long ms = 1578743412000L; - long timeOut = 90000; + long timeOut = 150000; int NUMBER_SIMULTANEOUS_INSERTS = 5; try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString @@ -243,6 +243,14 @@ public void testSqlServerBulkCopyCachingConnectionLevelMultiThreaded() throws Ex ((HashMap) bulkcopyCache).clear(); + TimerTask task = new TimerTask() { + public void run() { + ((HashMap) bulkcopyCache).clear(); + fail(TestResource.getResource("R_executionTooLong")); + } + }; + Timer timer = new Timer("Timer"); + timer.schedule(task, timeOut); // Run a timer to help us exit if we get deadlocked final CountDownLatch countDownLatch = new CountDownLatch(NUMBER_SIMULTANEOUS_INSERTS); Runnable runnable = () -> { From 428d3b218e7f8f551555cc7a6ea4e937597b788e Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Fri, 20 Sep 2024 00:25:15 -0700 Subject: [PATCH 23/30] More code cleanup --- .../microsoft/sqlserver/jdbc/SQLServerConnection.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index a0dbfaac6..0a1096c44 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3548,12 +3548,11 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu } sleepForInterval(fedauthRetryInterval); - int connectRetryIntervalInSeconds = (int) TimeUnit.SECONDS.toMillis(connectRetryInterval); + int retryIntervalSecs = (int) TimeUnit.SECONDS.toMillis(connectRetryInterval); - fedauthRetryInterval *= 2; // Double each time - if (fedauthRetryInterval > connectRetryIntervalInSeconds) { - fedauthRetryInterval = connectRetryIntervalInSeconds; // To a maximum of connectRetryInterval - } + // Double each time to a max of connectRetryInterval + fedauthRetryInterval = (fedauthRetryInterval >= retryIntervalSecs) + ? retryIntervalSecs : fedauthRetryInterval * 2; } } From 8d295e2cf333cbc00b2a2fdaac9a97d8973c4b66 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Fri, 20 Sep 2024 00:31:05 -0700 Subject: [PATCH 24/30] Fixed mistake --- .../sqlserver/jdbc/resiliency/BasicConnectionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java index d9b8e3e92..d4ba85fa5 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java @@ -68,7 +68,7 @@ public void testBasicConnectionAAD() throws Exception { basicReconnect("jdbc:sqlserver://" + azureServer + ";database=" + azureDatabase + ";user=" + azureUserName + ";password=" + azurePassword - + ";loginTimeout=90;Authentication=ActiveDirectoryPassword;"); + + ";loginTimeout=90;Authentication=ActiveDirectoryPassword"); retry = THROTTLE_RETRY_COUNT + 1; } catch (Exception e) { if (e.getMessage().matches(TestUtils.formatErrorMsg("R_crClientAllRecoveryAttemptsFailed"))) { From 617b97751def62851c7142a96c934ef272d62546 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Fri, 20 Sep 2024 00:33:37 -0700 Subject: [PATCH 25/30] Formatting --- .../sqlserver/jdbc/SQLServerConnection.java | 4 ++-- .../jdbc/unit/statement/BatchExecutionTest.java | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 0a1096c44..7f0a31b58 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3551,8 +3551,8 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu int retryIntervalSecs = (int) TimeUnit.SECONDS.toMillis(connectRetryInterval); // Double each time to a max of connectRetryInterval - fedauthRetryInterval = (fedauthRetryInterval >= retryIntervalSecs) - ? retryIntervalSecs : fedauthRetryInterval * 2; + fedauthRetryInterval = (fedauthRetryInterval >= retryIntervalSecs) ? retryIntervalSecs + : fedauthRetryInterval * 2; } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index 4f0b3917d..3551b5103 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -152,8 +152,8 @@ public void testSqlServerBulkCopyCachingConnectionLevel() throws Exception { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); long ms = 1578743412000L; - try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection( - connectionString + ";useBulkCopyForBatchInsert=true;cacheBulkCopyMetadata=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); + try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString + + ";useBulkCopyForBatchInsert=true;cacheBulkCopyMetadata=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); Statement stmt = con.createStatement()) { // Needs to be on a JDK version greater than 8 @@ -348,7 +348,7 @@ public void testValidTimezoneForTimestampBatchInsertWithBulkCopy() throws Except public void testValidTimezonesDstTimestampBatchInsertWithBulkCopy() throws Exception { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - for (String tzId: TimeZone.getAvailableIDs()) { + for (String tzId : TimeZone.getAvailableIDs()) { TimeZone.setDefault(TimeZone.getTimeZone(tzId)); long ms = 1696127400000L; // DST @@ -371,8 +371,8 @@ public void testValidTimezonesDstTimestampBatchInsertWithBulkCopy() throws Excep } // Insert Timestamp using bulkcopy for batch insert - try (Connection con = DriverManager.getConnection( - connectionString + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); + try (Connection con = DriverManager.getConnection(connectionString + + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); PreparedStatement pstmt = con.prepareStatement("INSERT INTO " + timestampTable1 + " VALUES(?)")) { Timestamp timestamp = new Timestamp(ms); @@ -419,8 +419,9 @@ public void testBatchInsertTimestampNoTimezoneDoubleConversion() throws Exceptio long ms = 1578743412000L; // Insert Timestamp using prepared statement when useBulkCopyForBatchInsert=true - try (Connection con = DriverManager.getConnection(connectionString - + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); Statement stmt = con.createStatement(); + try (Connection con = DriverManager.getConnection( + connectionString + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); + Statement stmt = con.createStatement(); PreparedStatement pstmt = con.prepareStatement("INSERT INTO " + timestampTable2 + " VALUES(?)")) { TestUtils.dropTableIfExists(timestampTable2, stmt); From 1de08c94807b7da9076fda9c5340bdaa379f2055 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Fri, 20 Sep 2024 06:52:45 -0700 Subject: [PATCH 26/30] Trying to resolve code cov --- .../jdbc/SQLServerConnectionTest.java | 18 ++++++++++-------- .../unit/statement/BatchExecutionTest.java | 13 ++++++------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index f0306d943..c01ce496d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -5,9 +5,9 @@ package com.microsoft.sqlserver.jdbc; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.io.Reader; @@ -508,7 +508,6 @@ private void testConnectCountInLoginAndCorrectRetryCount(int connectRetryCount) int minTimeInSecs = connectRetryInterval * (connectRetryCount - 1); assertTrue(totalTime > (minTimeInSecs * 1000L), TestResource.getResource("R_executionNotLong")); } - } } @@ -819,8 +818,9 @@ public void testIncorrectDatabase() throws SQLException { } catch (Exception e) { assertTrue( e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase")) - || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() - .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), + || (TestUtils.getProperty(connectionString, "msiClientId") != null + && e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), e.getMessage()); timerEnd = System.currentTimeMillis(); } @@ -851,8 +851,9 @@ public void testIncorrectUserName() throws SQLException { } catch (Exception e) { assertTrue( e.getMessage().contains(TestResource.getResource("R_loginFailed")) - || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() - .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), + || (TestUtils.getProperty(connectionString, "msiClientId") != null + && e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), e.getMessage()); timerEnd = System.currentTimeMillis(); } @@ -883,8 +884,9 @@ public void testIncorrectPassword() throws SQLException { } catch (Exception e) { assertTrue( e.getMessage().contains(TestResource.getResource("R_loginFailed")) - || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() - .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), + || (TestUtils.getProperty(connectionString, "msiClientId") != null + && e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_loginFailedMI").toLowerCase())), e.getMessage()); timerEnd = System.currentTimeMillis(); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index 3551b5103..34bb1a1f2 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -152,8 +152,8 @@ public void testSqlServerBulkCopyCachingConnectionLevel() throws Exception { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); long ms = 1578743412000L; - try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString - + ";useBulkCopyForBatchInsert=true;cacheBulkCopyMetadata=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); + try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection( + connectionString + ";useBulkCopyForBatchInsert=true;cacheBulkCopyMetadata=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); Statement stmt = con.createStatement()) { // Needs to be on a JDK version greater than 8 @@ -371,8 +371,8 @@ public void testValidTimezonesDstTimestampBatchInsertWithBulkCopy() throws Excep } // Insert Timestamp using bulkcopy for batch insert - try (Connection con = DriverManager.getConnection(connectionString - + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); + try (Connection con = DriverManager.getConnection( + connectionString + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); PreparedStatement pstmt = con.prepareStatement("INSERT INTO " + timestampTable1 + " VALUES(?)")) { Timestamp timestamp = new Timestamp(ms); @@ -419,9 +419,8 @@ public void testBatchInsertTimestampNoTimezoneDoubleConversion() throws Exceptio long ms = 1578743412000L; // Insert Timestamp using prepared statement when useBulkCopyForBatchInsert=true - try (Connection con = DriverManager.getConnection( - connectionString + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); - Statement stmt = con.createStatement(); + try (Connection con = DriverManager.getConnection(connectionString + + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); Statement stmt = con.createStatement(); PreparedStatement pstmt = con.prepareStatement("INSERT INTO " + timestampTable2 + " VALUES(?)")) { TestUtils.dropTableIfExists(timestampTable2, stmt); From 2ca351e1503b8db9a46477ab1106cee57699a9ab Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Fri, 20 Sep 2024 07:41:06 -0700 Subject: [PATCH 27/30] Update timeout value --- .../sqlserver/jdbc/unit/statement/BatchExecutionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index 34bb1a1f2..870abc2f0 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -218,7 +218,7 @@ public void testSqlServerBulkCopyCachingConnectionLevelMultiThreaded() throws Ex Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); long ms = 1578743412000L; - long timeOut = 150000; + long timeOut = 600000; int NUMBER_SIMULTANEOUS_INSERTS = 5; try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString From 3a763bb63d97593687c120e51d46e11a4844cc6a Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Fri, 20 Sep 2024 08:24:21 -0700 Subject: [PATCH 28/30] Remove stray fail --- .../sqlserver/jdbc/unit/statement/BatchExecutionTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index 870abc2f0..ba3f6e70c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -218,7 +218,7 @@ public void testSqlServerBulkCopyCachingConnectionLevelMultiThreaded() throws Ex Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); long ms = 1578743412000L; - long timeOut = 600000; + long timeOut = 30000; int NUMBER_SIMULTANEOUS_INSERTS = 5; try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString @@ -246,7 +246,6 @@ public void testSqlServerBulkCopyCachingConnectionLevelMultiThreaded() throws Ex TimerTask task = new TimerTask() { public void run() { ((HashMap) bulkcopyCache).clear(); - fail(TestResource.getResource("R_executionTooLong")); } }; Timer timer = new Timer("Timer"); From 47830db77d97a6af6f22c527761da84e0a8c03f9 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Tue, 24 Sep 2024 11:42:09 -0700 Subject: [PATCH 29/30] Revert changes to login retry interval. --- .../sqlserver/jdbc/SQLServerConnection.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 7f0a31b58..e8193ebcc 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -132,7 +132,7 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial /** * Back off interval in ms for retries */ - static final int BACKOFF_INTERVAL = 500; + static final int BACKOFF_INTERVAL = 100; /** Current limit for this particular connection. */ private Boolean enablePrepareOnFirstPreparedStatementCall = null; @@ -3528,10 +3528,9 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu // We only get here when we failed to connect, but are going to re-try // After trying to connect to both servers fails, sleep for a bit to prevent clogging - // the network with requests, then update sleep interval for next iteration (to a maximum specified by - // connectRetryInterval [default 10]). We have to sleep for every attempt in case of non-dbMirroring - // scenarios (including multisubnetfailover), whereas for dbMirroring, we sleep for every two attempts - // as each attempt is to a different server. + // the network with requests, then update sleep interval for next iteration (max 1 second interval) + // We have to sleep for every attempt in case of non-dbMirroring scenarios (including multisubnetfailover), + // Whereas for dbMirroring, we sleep for every two attempts as each attempt is to a different server. // Make sure there's enough time to do another retry if (!isDBMirroring || (isDBMirroring && (0 == attemptNumber % 2)) @@ -3547,12 +3546,7 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu + attemptNumber); } - sleepForInterval(fedauthRetryInterval); - int retryIntervalSecs = (int) TimeUnit.SECONDS.toMillis(connectRetryInterval); - - // Double each time to a max of connectRetryInterval - fedauthRetryInterval = (fedauthRetryInterval >= retryIntervalSecs) ? retryIntervalSecs - : fedauthRetryInterval * 2; + sleepForInterval(TimeUnit.SECONDS.toMillis(connectRetryInterval)); } } From ac63871dc1696b1b9f742e76bc19845d7a5cd015 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Tue, 24 Sep 2024 11:42:44 -0700 Subject: [PATCH 30/30] Remove added line. --- .../java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index e8193ebcc..e1e33747a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3531,7 +3531,6 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu // the network with requests, then update sleep interval for next iteration (max 1 second interval) // We have to sleep for every attempt in case of non-dbMirroring scenarios (including multisubnetfailover), // Whereas for dbMirroring, we sleep for every two attempts as each attempt is to a different server. - // Make sure there's enough time to do another retry if (!isDBMirroring || (isDBMirroring && (0 == attemptNumber % 2)) && (attemptNumber < connectRetryCount && connectRetryCount != 0) && timerRemaining(