From ea397d040ef5cffd7268d710a4852d5bdddbb20d Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Mon, 23 Sep 2024 14:28:52 -0700 Subject: [PATCH 1/8] Added back changes for connection part of CRL, which only affect exis 0296179 ting files. To allow for PR to be created. --- .../sqlserver/jdbc/SQLServerConnection.java | 17 +++++++++++++++-- 1 file changed, 15 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 903adc0b0..430313fec 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -2024,10 +2024,23 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio } throw e; } else { - // only retry if transient error + // Only retry if matches configured CRL rules, or transient error (if CRL is not in use) SQLServerError sqlServerError = e.getSQLServerError(); - if (!TransientError.isTransientError(sqlServerError)) { + if (null == sqlServerError) { throw e; + } else { + ConfigurableRetryRule rule = ConfigurableRetryLogic.getInstance() + .searchRuleSet(sqlServerError.getErrorNumber(), "connection"); + + if (null == rule) { + if (ConfigurableRetryLogic.getInstance().getReplaceFlag()) { + throw e; + } else { + if (!TransientError.isTransientError(sqlServerError)) { + throw e; + } + } + } } // check if there's time to retry, no point to wait if no time left From bd5c8eb4c71f53bc6278fc8d41ce66a428a022cd Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Tue, 24 Sep 2024 11:33:27 -0700 Subject: [PATCH 2/8] added more --- .../jdbc/ConfigurableRetryLogic.java | 106 ++++++++++++++---- .../sqlserver/jdbc/ConfigurableRetryRule.java | 21 +++- .../sqlserver/jdbc/ISQLServerDataSource.java | 16 +++ .../sqlserver/jdbc/SQLServerConnection.java | 22 ++++ .../sqlserver/jdbc/SQLServerDataSource.java | 32 ++++++ .../sqlserver/jdbc/SQLServerDriver.java | 5 +- .../sqlserver/jdbc/SQLServerResource.java | 3 +- .../sqlserver/jdbc/SQLServerStatement.java | 2 +- .../jdbc/SQLServerConnectionTest.java | 3 + .../ConfigurableRetryLogicTest.java | 2 +- .../RequestBoundaryMethodsTest.java | 2 + 11 files changed, 185 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java index ed5591034..49f2b7dc0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java @@ -39,6 +39,10 @@ public class ConfigurableRetryLogic { private static final String FORWARD_SLASH = "/"; private static final String EQUALS_SIGN = "="; private static final String RETRY_EXEC = "retryExec"; + private static final String RETRY_CONN = "retryConn"; + private static final String STATEMENT = "statement"; + private static final String CONNECTION = "connection"; + private static boolean replaceFlag = false; // Are we replacing the list of transient errors? /** * The time the properties file was last modified. */ @@ -52,14 +56,23 @@ public class ConfigurableRetryLogic { */ private static final AtomicReference lastQuery = new AtomicReference<>(""); /** - * The previously read rules from the connection string. + * The previously read statement rules from the connection string. */ - private static final AtomicReference prevRulesFromConnectionString = new AtomicReference<>(""); + private static final AtomicReference prevStmtRulesFromConnString = new AtomicReference<>(""); + /** + * The previously read connection rules from the connection string. + */ + private static final AtomicReference prevConnRulesFromConnString = new AtomicReference<>(""); /** * The list of statement retry rules. */ private static final AtomicReference> stmtRules = new AtomicReference<>( new HashMap<>()); + /** + * The list of connection retry rules. + */ + private static final AtomicReference> connRules = new AtomicReference<>( + new HashMap<>()); private static ConfigurableRetryLogic singleInstance; /** @@ -70,7 +83,8 @@ public class ConfigurableRetryLogic { */ private ConfigurableRetryLogic() throws SQLServerException { timeLastRead.compareAndSet(0, new Date().getTime()); - setUpRules(null); + setUpRules(null, STATEMENT); + setUpRules(null, CONNECTION); } /** @@ -102,7 +116,8 @@ public static ConfigurableRetryLogic getInstance() throws SQLServerException { /** * If it has been INTERVAL_BETWEEN_READS_IN_MS (30 secs) since last read, see if we last did a file read, if so - * only reread if the file has been modified. If no file read, set up rules using the prev. connection string rules. + * only reread if the file has been modified. If no file read, set up rules using the previous connection + * string (statement and connection) rules * * @throws SQLServerException * when an exception occurs @@ -116,10 +131,12 @@ private static void refreshRuleSet() throws SQLServerException { // If timeLastModified is set, we previously read from file, so we setUpRules also reading from file File f = new File(getCurrentClassPath()); if (f.lastModified() != timeLastModified.get()) { - setUpRules(null); + setUpRules(null, STATEMENT); + setUpRules(null, CONNECTION); } } else { - setUpRules(prevRulesFromConnectionString.get()); + setUpRules(prevStmtRulesFromConnString.get(), STATEMENT); + setUpRules(prevConnRulesFromConnString.get(), CONNECTION); } } } @@ -133,8 +150,10 @@ private static void refreshRuleSet() throws SQLServerException { * when an exception occurs */ void setFromConnectionString(String newRules) throws SQLServerException { - prevRulesFromConnectionString.set(newRules); - setUpRules(prevRulesFromConnectionString.get()); + prevStmtRulesFromConnString.set(newRules); + prevConnRulesFromConnString.set(newRules); + setUpRules(prevStmtRulesFromConnString.get(), STATEMENT); + setUpRules(prevConnRulesFromConnString.get(), CONNECTION); } /** @@ -160,23 +179,28 @@ String getLastQuery() { * Sets up rules based on either connection string option or file read. * * @param cxnStrRules - * if null, rules are constructed from file, else, this parameter is used to construct rules + * if null, rules are constructed from file, else, this parameter is used to construct rules + * @param ruleType + * either "statement" or "connection" for statement or connection rules respectively * @throws SQLServerException - * if an exception occurs + * if an exception occurs */ - private static void setUpRules(String cxnStrRules) throws SQLServerException { + private static void setUpRules(String cxnStrRules, String ruleType) throws SQLServerException { LinkedList temp; - - stmtRules.set(new HashMap<>()); lastQuery.set(""); if (cxnStrRules == null || cxnStrRules.isEmpty()) { - temp = readFromFile(); + if (ruleType.equals(STATEMENT)) { + temp = readFromFile(RETRY_EXEC); + } else { + temp = readFromFile(RETRY_CONN); + } + } else { temp = new LinkedList<>(); Collections.addAll(temp, cxnStrRules.split(SEMI_COLON)); } - createRules(temp); + createRules(temp, ruleType); } /** @@ -184,10 +208,13 @@ private static void setUpRules(String cxnStrRules) throws SQLServerException { * * @param listOfRules * the list of rules, as a String LinkedList + * @param ruleType + * the type of rule; either "statement" or "connection * @throws SQLServerException * if unable to create rules from the inputted list */ - private static void createRules(LinkedList listOfRules) throws SQLServerException { + private static void createRules(LinkedList listOfRules, String ruleType) throws SQLServerException { + connRules.set(new HashMap<>()); stmtRules.set(new HashMap<>()); for (String potentialRule : listOfRules) { @@ -198,10 +225,29 @@ private static void createRules(LinkedList listOfRules) throws SQLServer for (String retryError : arr) { ConfigurableRetryRule splitRule = new ConfigurableRetryRule(retryError, rule); - stmtRules.get().put(Integer.parseInt(splitRule.getError()), splitRule); + if (rule.isConnection) { +// if (rule.replaceExisting) { +// if (!replaceFlag) { +// connRules.set(new HashMap<>()); +// } +// replaceFlag = true; +// } + connRules.get().put(Integer.parseInt(splitRule.getError()), splitRule); + } else { + stmtRules.get().put(Integer.parseInt(splitRule.getError()), splitRule); + } + } } else { - stmtRules.get().put(Integer.parseInt(rule.getError()), rule); + if (rule.isConnection) { +// if (rule.replaceExisting) { +// connRules.set(new HashMap<>()); +// replaceFlag = true; +// } + connRules.get().put(Integer.parseInt(rule.getError()), rule); + } else { + stmtRules.get().put(Integer.parseInt(rule.getError()), rule); + } } } } @@ -241,7 +287,7 @@ private static String getCurrentClassPath() throws SQLServerException { * @throws SQLServerException * if unable to read from the file */ - private static LinkedList readFromFile() throws SQLServerException { + private static LinkedList readFromFile(String connectionStringProperty) throws SQLServerException { String filePath = getCurrentClassPath(); LinkedList list = new LinkedList<>(); @@ -250,7 +296,7 @@ private static LinkedList readFromFile() throws SQLServerException { try (BufferedReader buffer = new BufferedReader(new FileReader(f))) { String readLine; while ((readLine = buffer.readLine()) != null) { - if (readLine.startsWith(RETRY_EXEC)) { + if (readLine.startsWith(connectionStringProperty)) { // Either "retryExec" or "retryConn" String value = readLine.split(EQUALS_SIGN)[1]; Collections.addAll(list, value.split(SEMI_COLON)); } @@ -280,13 +326,25 @@ private static LinkedList readFromFile() throws SQLServerException { * @throws SQLServerException * when an exception occurs */ - ConfigurableRetryRule searchRuleSet(int ruleToSearchFor) throws SQLServerException { + ConfigurableRetryRule searchRuleSet(int ruleToSearchFor, String ruleSet) throws SQLServerException { refreshRuleSet(); - for (Map.Entry entry : stmtRules.get().entrySet()) { - if (entry.getKey() == ruleToSearchFor) { - return entry.getValue(); + if (ruleSet.equals(STATEMENT)) { + for (Map.Entry entry : stmtRules.get().entrySet()) { + if (entry.getKey() == ruleToSearchFor) { + return entry.getValue(); + } + } + } else { + for (Map.Entry entry : connRules.get().entrySet()) { + if (entry.getKey() == ruleToSearchFor) { + return entry.getValue(); + } } } return null; } + + boolean getReplaceFlag() { + return replaceFlag; + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java index f52df8d8d..fd3b258da 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java @@ -25,6 +25,8 @@ class ConfigurableRetryRule { private int retryCount = 1; private String retryQueries = ""; private String retryError; + boolean isConnection = false; + boolean replaceExisting = false; private ArrayList waitTimes = new ArrayList<>(); @@ -70,6 +72,18 @@ private void copyFromRule(ConfigurableRetryRule baseRule) { this.retryCount = baseRule.retryCount; this.retryQueries = baseRule.retryQueries; this.waitTimes = baseRule.waitTimes; + this.isConnection = baseRule.isConnection; + } + + private String appendOrReplace(String retryError) { + if (retryError.charAt(0) == '+') { + replaceExisting = false; + StringUtils.isNumeric(retryError.substring(1)); + return retryError.substring(1); + } else { + replaceExisting = true; + return retryError; + } } /** @@ -152,7 +166,12 @@ private void checkParameter(String value) throws SQLServerException { * if a rule or parameter has invalid inputs */ private void addElements(String[] rule) throws SQLServerException { - if (rule.length == 2 || rule.length == 3) { + if (rule.length == 1) { + String errorWithoutOptionalPrefix = appendOrReplace(rule[0]); + checkParameter(errorWithoutOptionalPrefix); + isConnection = true; + retryError = errorWithoutOptionalPrefix; + } else if (rule.length == 2 || rule.length == 3) { checkParameter(rule[0]); retryError = rule[0]; String[] timings = rule[1].split(COMMA); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java index 66fc33744..ec7067220 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java @@ -1363,6 +1363,22 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource { */ String getRetryExec(); + /** + * Returns value of 'retryConn' from Connection String. + * + * @param retryConn + * Set of rules used for connection retry + */ + void setRetryConn(String retryConn); + + /** + * Sets the value for 'retryConn' property + * + * @return retryConn + * String value + */ + String getRetryConn(); + /** * useFlexibleCallableStatements is temporarily removed. This is meant as a no-op. * diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 430313fec..543908b92 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -1086,6 +1086,28 @@ public void setRetryExec(String retryExec) { this.retryExec = retryExec; } + private String retryConn = SQLServerDriverStringProperty.RETRY_CONN.getDefaultValue(); + + /** + * Returns the set of configurable connection retry rules set in retryConn + * + * @return + * A string containing statement retry rules. + */ + public String getRetryConn() { + return retryConn; + } + + /** + * Sets the list of configurable connection retry rules, for the given connection, in retryConn. + * + * @param retryConn + * The list of retry rules to set, as a string. + */ + public void setRetryConn(String retryConn) { + this.retryConn = retryConn; + } + /** Session Recovery Object */ private transient IdleConnectionResiliency sessionRecovery = new IdleConnectionResiliency(this); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index 6136cffa0..da7688e60 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -1383,16 +1383,48 @@ public boolean getCalcBigDecimalPrecision() { SQLServerDriverBooleanProperty.CALC_BIG_DECIMAL_PRECISION.getDefaultValue()); } + /** + * Sets the 'retryExec' setting. + * + * @param retryExec + * String property giving the custom statement retry rules to use for configurable retry logic + */ @Override public void setRetryExec(String retryExec) { setStringProperty(connectionProps, SQLServerDriverStringProperty.RETRY_EXEC.toString(), retryExec); } + /** + * Returns the value for 'retryExec'. + * + * @return retryExec String value + */ @Override public String getRetryExec() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.RETRY_EXEC.toString(), null); } + /** + * Sets the 'retryConn' setting. + * + * @param retryConn + * String property giving the custom connection retry rules to use for configurable retry logic + */ + @Override + public void setRetryConn(String retryConn) { + setStringProperty(connectionProps, SQLServerDriverStringProperty.RETRY_CONN.toString(), retryConn); + } + + /** + * Returns the value for 'retryConn'. + * + * @return retryConn String value + */ + @Override + public String getRetryConn() { + return getStringProperty(connectionProps, SQLServerDriverStringProperty.RETRY_CONN.toString(), null); + } + /** * Sets a property string value. * diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 1c9abc000..e4b1d59ee 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -611,7 +611,8 @@ enum SQLServerDriverStringProperty { SERVER_CERTIFICATE("serverCertificate", ""), DATETIME_DATATYPE("datetimeParameterType", DatetimeType.DATETIME2.toString()), ACCESS_TOKEN_CALLBACK_CLASS("accessTokenCallbackClass", ""), - RETRY_EXEC("retryExec", ""); + RETRY_EXEC("retryExec", ""), + RETRY_CONN("retryConn", ""); private final String name; private final String defaultValue; @@ -855,6 +856,8 @@ public final class SQLServerDriver implements java.sql.Driver { SQLServerDriverStringProperty.ACCESS_TOKEN_CALLBACK_CLASS.getDefaultValue(), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.RETRY_EXEC.toString(), SQLServerDriverStringProperty.RETRY_EXEC.getDefaultValue(), false, null), + new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.RETRY_CONN.toString(), + SQLServerDriverStringProperty.RETRY_CONN.getDefaultValue(), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.REPLICATION.toString(), Boolean.toString(SQLServerDriverBooleanProperty.REPLICATION.getDefaultValue()), false, TRUE_FALSE), new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.toString(), diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 55a5a5fc0..c9d875e58 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -517,7 +517,8 @@ protected Object[][] getContents() { {"R_InvalidCSVQuotes", "Failed to parse the CSV file, verify that the fields are correctly enclosed in double quotes."}, {"R_TokenRequireUrl", "Token credentials require a URL using the HTTPS protocol scheme."}, {"R_calcBigDecimalPrecisionPropertyDescription", "Indicates whether the driver should calculate precision for big decimal values."}, - {"R_retryExecPropertyDescription", "List of rules to follow for configurable retry logic."}, + {"R_retryExecPropertyDescription", "List of statement retry rules to follow for configurable retry logic."}, + {"R_retryConnPropertyDescription", "List of connection retry rules to follow for configurable retry logic."}, {"R_maxResultBufferPropertyDescription", "Determines maximum amount of bytes that can be read during retrieval of result set"}, {"R_maxResultBufferInvalidSyntax", "Invalid syntax: {0} in maxResultBuffer parameter."}, {"R_maxResultBufferNegativeParameterValue", "MaxResultBuffer must have positive value: {0}."}, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 883320640..fef51bc6a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -256,7 +256,7 @@ final void executeStatement(TDSCommand newStmtCmd) throws SQLServerException, SQ ConfigurableRetryRule rule = null; if (null != sqlServerError) { - rule = crl.searchRuleSet(e.getSQLServerError().getErrorNumber()); + rule = crl.searchRuleSet(e.getSQLServerError().getErrorNumber(), "statement"); } // If there is a rule for this error AND we still have retries remaining THEN we can proceed, otherwise diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index 3b7fe1dc7..33654216f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -212,6 +212,9 @@ public void testDataSource() throws SQLServerException { ds.setRetryExec(stringPropValue); assertEquals(stringPropValue, ds.getRetryExec(), TestResource.getResource("R_valuesAreDifferent")); + ds.setRetryConn(stringPropValue); + assertEquals(stringPropValue, ds.getRetryConn(), TestResource.getResource("R_valuesAreDifferent")); + ds.setServerCertificate(stringPropValue); assertEquals(stringPropValue, ds.getServerCertificate(), TestResource.getResource("R_valuesAreDifferent")); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java index 169986a0a..e4d3b1c48 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java @@ -32,7 +32,7 @@ /** - * Test statement retry for configurable retry logic. + * Test connection and statement retry for configurable retry logic. */ public class ConfigurableRetryLogicTest extends AbstractTest { /** diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java index 8d3a62788..402cccc8c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java @@ -523,6 +523,8 @@ private List getVerifiedMethodNames() { verifiedMethodNames.add("removeBeforeReconnectListener"); verifiedMethodNames.add("getRetryExec"); verifiedMethodNames.add("setRetryExec"); + verifiedMethodNames.add("getRetryConn"); + verifiedMethodNames.add("setRetryConn"); verifiedMethodNames.add("getUseFlexibleCallableStatements"); verifiedMethodNames.add("setUseFlexibleCallableStatements"); return verifiedMethodNames; From 08d6cc42ba26a70ae99ff3efd087a8801206e957 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 25 Sep 2024 14:50:37 -0700 Subject: [PATCH 3/8] Added tests --- .../ConfigurableRetryLogicTest.java | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java index e4d3b1c48..8dfafaeea 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java @@ -172,6 +172,29 @@ public void testStatementRetryWithShortQueryTimeout(String addedRetryParams) thr } } + /** + * Tests connection retry. Used in other tests. + * + * @throws Exception + * if unable to connect or execute against db + */ + public void testConnectionRetry(String replacedDbName, String addedRetryParams) throws Exception { + String cxnString = connectionString + addedRetryParams; + cxnString = TestUtils.addOrOverrideProperty(cxnString, "database", replacedDbName); + + try (Connection conn = DriverManager.getConnection(cxnString); Statement s = conn.createStatement()) { + try { + fail(TestResource.getResource("R_expectedFailPassed")); + } catch (Exception e) { + System.out.println("blah"); + assertTrue(e.getMessage().startsWith("There is already an object"), + TestResource.getResource("R_unexpectedExceptionContent") + ": " + e.getMessage()); + } finally { + dropTable(s); + } + } + } + /** * Tests that the correct number of retries are happening for all statement scenarios. Tests are expected to take * a minimum of the sum of whatever has been defined for the waiting intervals, and maximum of the previous sum @@ -445,6 +468,94 @@ public void testRetryChange() throws Exception { } } + /** + * Tests that the correct number of retries are happening for all connection scenarios. Tests are expected to take + * a minimum of the sum of whatever has been defined for the waiting intervals, and maximum of the previous sum + * plus some amount of time to account for test environment slowness. + */ + @Test + public void connectionTimingTest() { + long totalTime; + long timerStart = System.currentTimeMillis(); + long expectedMaxTime = 10; + + // No retries since CRL rules override, expected time ~1 second + try { + testConnectionRetry("blah", "retryConn={9999};"); + } catch (Exception e) { + assertTrue( + (e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) + || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() + .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())) + || ((isSqlAzure() || isSqlAzureDW()) && e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_connectTimedOut").toLowerCase())), + e.getMessage()); + + if (e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) { + // Only check the timing if the correct error, "cannot open database", is returned. + totalTime = System.currentTimeMillis() - timerStart; + assertTrue(totalTime < TimeUnit.SECONDS.toMillis(expectedMaxTime), + "total time: " + totalTime + ", expected time: " + TimeUnit.SECONDS.toMillis(expectedMaxTime)); + } + } + + timerStart = System.currentTimeMillis(); + long expectedMinTime = 20; + expectedMaxTime = 30; + + // (0s attempt + 10s wait + 0s attempt) * 2 due to current driver bug = expected 20s execution time + try { + testConnectionRetry("blah", "retryConn={4060};"); + } catch (Exception e) { + assertTrue( + (e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) + || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() + .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())) + || ((isSqlAzure() || isSqlAzureDW()) && e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_connectTimedOut").toLowerCase())), + e.getMessage()); + + if (e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) { + // Only check the timing if the correct error, "cannot open database", is returned. + totalTime = System.currentTimeMillis() - timerStart; + assertTrue(totalTime < TimeUnit.SECONDS.toMillis(expectedMaxTime), "total time: " + totalTime + + ", expected max time: " + TimeUnit.SECONDS.toMillis(expectedMaxTime)); + assertTrue(totalTime > TimeUnit.SECONDS.toMillis(expectedMinTime), "total time: " + totalTime + + ", expected min time: " + TimeUnit.SECONDS.toMillis(expectedMinTime)); + } + } + + timerStart = System.currentTimeMillis(); + + // Append should work the same way + try { + testConnectionRetry("blah", "retryConn={+4060,4070};"); + } catch (Exception e) { + assertTrue( + (e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) + || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() + .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())) + || ((isSqlAzure() || isSqlAzureDW()) && e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_connectTimedOut").toLowerCase())), + e.getMessage()); + + if (e.getMessage().toLowerCase() + .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) { + // Only check the timing if the correct error, "cannot open database", is returned. + totalTime = System.currentTimeMillis() - timerStart; + assertTrue(totalTime < TimeUnit.SECONDS.toMillis(expectedMaxTime), "total time: " + totalTime + + ", expected max time: " + TimeUnit.SECONDS.toMillis(expectedMaxTime)); + assertTrue(totalTime > TimeUnit.SECONDS.toMillis(expectedMinTime), "total time: " + totalTime + + ", expected min time: " + TimeUnit.SECONDS.toMillis(expectedMinTime)); + } + } + } + /** * Creates table for use in ConfigurableRetryLogic tests. * From 2220ba9d115a3d5054a2b57deaa388ace4dac6d1 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Sat, 28 Sep 2024 16:07:51 -0700 Subject: [PATCH 4/8] Add another test; see if I can still push to GitHub --- .../ConfigurableRetryLogicTest.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java index 8dfafaeea..9e2c80c19 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java @@ -5,9 +5,6 @@ package com.microsoft.sqlserver.jdbc.configurableretry; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - import java.io.File; import java.io.FileWriter; import java.sql.CallableStatement; @@ -30,6 +27,8 @@ import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; +import static org.junit.jupiter.api.Assertions.*; + /** * Test connection and statement retry for configurable retry logic. @@ -66,6 +65,31 @@ public void testRetryExecConnectionStringOption() throws Exception { String test = conn.getRetryExec(); assertTrue(test.isEmpty()); conn.setRetryExec("{2714:3,2*2:CREATE;2715:1,3}"); + test = conn.getRetryExec(); + assertFalse(test.isEmpty()); + try { + PreparedStatement ps = conn.prepareStatement("create table " + CRLTestTable + " (c1 int null);"); + createTable(s); + ps.execute(); + Assertions.fail(TestResource.getResource("R_expectedFailPassed")); + } catch (SQLServerException e) { + assertTrue(e.getMessage().startsWith("There is already an object"), + TestResource.getResource("R_unexpectedExceptionContent") + ": " + e.getMessage()); + } finally { + dropTable(s); + } + } + } + + @Test + public void testRetryConnConnectionStringOption() throws Exception { + try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString); + Statement s = conn.createStatement()) { + String test = conn.getRetryConn(); + assertTrue(test.isEmpty()); + conn.setRetryConn("{4060}"); + test = conn.getRetryConn(); + assertFalse(test.isEmpty()); try { PreparedStatement ps = conn.prepareStatement("create table " + CRLTestTable + " (c1 int null);"); createTable(s); From 80c563d9d79cb2c1e4a81c40c227b177f1d64bc3 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 2 Oct 2024 13:37:05 -0700 Subject: [PATCH 5/8] Added more --- .../jdbc/ConfigurableRetryLogic.java | 33 +++++++++++-------- .../sqlserver/jdbc/SQLServerConnection.java | 11 ++++++- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java index 49f2b7dc0..c21a2f449 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java @@ -142,17 +142,28 @@ private static void refreshRuleSet() throws SQLServerException { } /** - * Sets rules given from connection string. + * Sets statement rules given from connection string. * * @param newRules * the new rules to use * @throws SQLServerException * when an exception occurs */ - void setFromConnectionString(String newRules) throws SQLServerException { + void setStatementRulesFromConnectionString(String newRules) throws SQLServerException { prevStmtRulesFromConnString.set(newRules); - prevConnRulesFromConnString.set(newRules); setUpRules(prevStmtRulesFromConnString.get(), STATEMENT); + } + + /** + * Sets connection rules given from connection string. + * + * @param newRules + * the new rules to use + * @throws SQLServerException + * when an exception occurs + */ + void setConnectionRulesFromConnectionString(String newRules) throws SQLServerException { + prevConnRulesFromConnString.set(newRules); setUpRules(prevConnRulesFromConnString.get(), CONNECTION); } @@ -226,12 +237,9 @@ private static void createRules(LinkedList listOfRules, String ruleType) for (String retryError : arr) { ConfigurableRetryRule splitRule = new ConfigurableRetryRule(retryError, rule); if (rule.isConnection) { -// if (rule.replaceExisting) { -// if (!replaceFlag) { -// connRules.set(new HashMap<>()); -// } -// replaceFlag = true; -// } + if (rule.replaceExisting) { + replaceFlag = true; + } connRules.get().put(Integer.parseInt(splitRule.getError()), splitRule); } else { stmtRules.get().put(Integer.parseInt(splitRule.getError()), splitRule); @@ -240,10 +248,9 @@ private static void createRules(LinkedList listOfRules, String ruleType) } } else { if (rule.isConnection) { -// if (rule.replaceExisting) { -// connRules.set(new HashMap<>()); -// replaceFlag = true; -// } + if (rule.replaceExisting) { + replaceFlag = true; + } connRules.get().put(Integer.parseInt(rule.getError()), rule); } else { stmtRules.get().put(Integer.parseInt(rule.getError()), rule); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 543908b92..9c80f080b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -2402,7 +2402,16 @@ Connection connectInternal(Properties propsIn, activeConnectionProperties.setProperty(sPropKey, sPropValue); } retryExec = sPropValue; - ConfigurableRetryLogic.getInstance().setFromConnectionString(sPropValue); + ConfigurableRetryLogic.getInstance().setStatementRulesFromConnectionString(sPropValue); + + sPropKey = SQLServerDriverStringProperty.RETRY_CONN.toString(); + sPropValue = activeConnectionProperties.getProperty(sPropKey); + if (null == sPropValue) { + sPropValue = SQLServerDriverStringProperty.RETRY_CONN.getDefaultValue(); + activeConnectionProperties.setProperty(sPropKey, sPropValue); + } + retryConn = sPropValue; + ConfigurableRetryLogic.getInstance().setConnectionRulesFromConnectionString(sPropValue); sPropKey = SQLServerDriverBooleanProperty.CALC_BIG_DECIMAL_PRECISION.toString(); sPropValue = activeConnectionProperties.getProperty(sPropKey); From bde0392c4f7c9d4e76c5b7aed3206972c5ba876d Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 2 Oct 2024 14:19:31 -0700 Subject: [PATCH 6/8] Decoupled statement and connection parts to ensure tests pass. --- .../jdbc/ConfigurableRetryLogic.java | 88 +++++++++++-------- .../sqlserver/jdbc/ConfigurableRetryRule.java | 2 +- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java index c21a2f449..3b9e1734b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java @@ -41,7 +41,6 @@ public class ConfigurableRetryLogic { private static final String RETRY_EXEC = "retryExec"; private static final String RETRY_CONN = "retryConn"; private static final String STATEMENT = "statement"; - private static final String CONNECTION = "connection"; private static boolean replaceFlag = false; // Are we replacing the list of transient errors? /** * The time the properties file was last modified. @@ -83,8 +82,8 @@ public class ConfigurableRetryLogic { */ private ConfigurableRetryLogic() throws SQLServerException { timeLastRead.compareAndSet(0, new Date().getTime()); - setUpRules(null, STATEMENT); - setUpRules(null, CONNECTION); + setUpStatementRules(null); + setUpConnectionRules(null); } /** @@ -131,12 +130,12 @@ private static void refreshRuleSet() throws SQLServerException { // If timeLastModified is set, we previously read from file, so we setUpRules also reading from file File f = new File(getCurrentClassPath()); if (f.lastModified() != timeLastModified.get()) { - setUpRules(null, STATEMENT); - setUpRules(null, CONNECTION); + setUpStatementRules(null); + setUpConnectionRules(null); } } else { - setUpRules(prevStmtRulesFromConnString.get(), STATEMENT); - setUpRules(prevConnRulesFromConnString.get(), CONNECTION); + setUpStatementRules(prevStmtRulesFromConnString.get()); + setUpConnectionRules(prevConnRulesFromConnString.get()); } } } @@ -151,7 +150,7 @@ private static void refreshRuleSet() throws SQLServerException { */ void setStatementRulesFromConnectionString(String newRules) throws SQLServerException { prevStmtRulesFromConnString.set(newRules); - setUpRules(prevStmtRulesFromConnString.get(), STATEMENT); + setUpStatementRules(prevStmtRulesFromConnString.get()); } /** @@ -164,7 +163,7 @@ void setStatementRulesFromConnectionString(String newRules) throws SQLServerExce */ void setConnectionRulesFromConnectionString(String newRules) throws SQLServerException { prevConnRulesFromConnString.set(newRules); - setUpRules(prevConnRulesFromConnString.get(), CONNECTION); + setUpConnectionRules(prevConnRulesFromConnString.get()); } /** @@ -191,27 +190,37 @@ String getLastQuery() { * * @param cxnStrRules * if null, rules are constructed from file, else, this parameter is used to construct rules - * @param ruleType - * either "statement" or "connection" for statement or connection rules respectively * @throws SQLServerException * if an exception occurs */ - private static void setUpRules(String cxnStrRules, String ruleType) throws SQLServerException { + private static void setUpStatementRules(String cxnStrRules) throws SQLServerException { LinkedList temp; + + stmtRules.set(new HashMap<>()); lastQuery.set(""); if (cxnStrRules == null || cxnStrRules.isEmpty()) { - if (ruleType.equals(STATEMENT)) { - temp = readFromFile(RETRY_EXEC); - } else { - temp = readFromFile(RETRY_CONN); - } + temp = readFromFile(RETRY_EXEC); + } else { + temp = new LinkedList<>(); + Collections.addAll(temp, cxnStrRules.split(SEMI_COLON)); + } + createStatementRules(temp); + } + private static void setUpConnectionRules(String cxnStrRules) throws SQLServerException { + LinkedList temp; + + connRules.set(new HashMap<>()); + lastQuery.set(""); + + if (cxnStrRules == null || cxnStrRules.isEmpty()) { + temp = readFromFile(RETRY_CONN); } else { temp = new LinkedList<>(); Collections.addAll(temp, cxnStrRules.split(SEMI_COLON)); } - createRules(temp, ruleType); + createConnectionRules(temp); } /** @@ -219,13 +228,10 @@ private static void setUpRules(String cxnStrRules, String ruleType) throws SQLSe * * @param listOfRules * the list of rules, as a String LinkedList - * @param ruleType - * the type of rule; either "statement" or "connection * @throws SQLServerException * if unable to create rules from the inputted list */ - private static void createRules(LinkedList listOfRules, String ruleType) throws SQLServerException { - connRules.set(new HashMap<>()); + private static void createStatementRules(LinkedList listOfRules) throws SQLServerException { stmtRules.set(new HashMap<>()); for (String potentialRule : listOfRules) { @@ -236,25 +242,33 @@ private static void createRules(LinkedList listOfRules, String ruleType) for (String retryError : arr) { ConfigurableRetryRule splitRule = new ConfigurableRetryRule(retryError, rule); - if (rule.isConnection) { - if (rule.replaceExisting) { - replaceFlag = true; - } - connRules.get().put(Integer.parseInt(splitRule.getError()), splitRule); - } else { - stmtRules.get().put(Integer.parseInt(splitRule.getError()), splitRule); + stmtRules.get().put(Integer.parseInt(splitRule.getError()), splitRule); } + } else { + stmtRules.get().put(Integer.parseInt(rule.getError()), rule); + } + } + } + + private static void createConnectionRules(LinkedList listOfRules) throws SQLServerException { + connRules.set(new HashMap<>()); + replaceFlag = false; + + for (String potentialRule : listOfRules) { + ConfigurableRetryRule rule = new ConfigurableRetryRule(potentialRule); + if (rule.replaceExisting) { + replaceFlag = true; + } + if (rule.getError().contains(COMMA)) { + String[] arr = rule.getError().split(COMMA); + + for (String retryError : arr) { + ConfigurableRetryRule splitRule = new ConfigurableRetryRule(retryError, rule); + connRules.get().put(Integer.parseInt(splitRule.getError()), splitRule); } } else { - if (rule.isConnection) { - if (rule.replaceExisting) { - replaceFlag = true; - } - connRules.get().put(Integer.parseInt(rule.getError()), rule); - } else { - stmtRules.get().put(Integer.parseInt(rule.getError()), rule); - } + connRules.get().put(Integer.parseInt(rule.getError()), rule); } } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java index fd3b258da..518a111be 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java @@ -76,7 +76,7 @@ private void copyFromRule(ConfigurableRetryRule baseRule) { } private String appendOrReplace(String retryError) { - if (retryError.charAt(0) == '+') { + if (retryError.startsWith(PLUS_SIGN)) { replaceExisting = false; StringUtils.isNumeric(retryError.substring(1)); return retryError.substring(1); From 2aea6ec88f29fad2e58954294eedbcdf6960aced Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Wed, 2 Oct 2024 15:09:12 -0700 Subject: [PATCH 7/8] Cleanup. --- .../jdbc/ConfigurableRetryLogic.java | 6 ++--- .../ConfigurableRetryLogicTest.java | 27 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java index 3b9e1734b..df9e6b956 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java @@ -189,9 +189,9 @@ String getLastQuery() { * Sets up rules based on either connection string option or file read. * * @param cxnStrRules - * if null, rules are constructed from file, else, this parameter is used to construct rules + * if null, rules are constructed from file, else, this parameter is used to construct rules * @throws SQLServerException - * if an exception occurs + * if an exception occurs */ private static void setUpStatementRules(String cxnStrRules) throws SQLServerException { LinkedList temp; @@ -243,7 +243,7 @@ private static void createStatementRules(LinkedList listOfRules) throws for (String retryError : arr) { ConfigurableRetryRule splitRule = new ConfigurableRetryRule(retryError, rule); stmtRules.get().put(Integer.parseInt(splitRule.getError()), splitRule); - } + } } else { stmtRules.get().put(Integer.parseInt(rule.getError()), rule); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java index 9e2c80c19..e5590d7cf 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java @@ -5,6 +5,10 @@ package com.microsoft.sqlserver.jdbc.configurableretry; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + import java.io.File; import java.io.FileWriter; import java.sql.CallableStatement; @@ -27,8 +31,6 @@ import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; -import static org.junit.jupiter.api.Assertions.*; - /** * Test connection and statement retry for configurable retry logic. @@ -511,13 +513,12 @@ public void connectionTimingTest() { (e.getMessage().toLowerCase() .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() - .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())) + .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())) || ((isSqlAzure() || isSqlAzureDW()) && e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_connectTimedOut").toLowerCase())), + .contains(TestResource.getResource("R_connectTimedOut").toLowerCase())), e.getMessage()); - if (e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) { + if (e.getMessage().toLowerCase().contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) { // Only check the timing if the correct error, "cannot open database", is returned. totalTime = System.currentTimeMillis() - timerStart; assertTrue(totalTime < TimeUnit.SECONDS.toMillis(expectedMaxTime), @@ -537,13 +538,12 @@ public void connectionTimingTest() { (e.getMessage().toLowerCase() .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() - .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())) + .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())) || ((isSqlAzure() || isSqlAzureDW()) && e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_connectTimedOut").toLowerCase())), + .contains(TestResource.getResource("R_connectTimedOut").toLowerCase())), e.getMessage()); - if (e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) { + if (e.getMessage().toLowerCase().contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) { // Only check the timing if the correct error, "cannot open database", is returned. totalTime = System.currentTimeMillis() - timerStart; assertTrue(totalTime < TimeUnit.SECONDS.toMillis(expectedMaxTime), "total time: " + totalTime @@ -563,13 +563,12 @@ public void connectionTimingTest() { (e.getMessage().toLowerCase() .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage() - .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())) + .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase())) || ((isSqlAzure() || isSqlAzureDW()) && e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_connectTimedOut").toLowerCase())), + .contains(TestResource.getResource("R_connectTimedOut").toLowerCase())), e.getMessage()); - if (e.getMessage().toLowerCase() - .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) { + if (e.getMessage().toLowerCase().contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) { // Only check the timing if the correct error, "cannot open database", is returned. totalTime = System.currentTimeMillis() - timerStart; assertTrue(totalTime < TimeUnit.SECONDS.toMillis(expectedMaxTime), "total time: " + totalTime From 33f0b934dcacb4fa8be04b8a47d0a53298904992 Mon Sep 17 00:00:00 2001 From: Jeff Wasty Date: Tue, 12 Nov 2024 09:21:57 -0800 Subject: [PATCH 8/8] Changed tests to account for new changes to retry logic. --- .../configurableretry/ConfigurableRetryLogicTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java index e5590d7cf..3f522080d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/configurableretry/ConfigurableRetryLogicTest.java @@ -527,12 +527,12 @@ public void connectionTimingTest() { } timerStart = System.currentTimeMillis(); - long expectedMinTime = 20; - expectedMaxTime = 30; + long expectedMinTime = 10; + expectedMaxTime = 35; - // (0s attempt + 10s wait + 0s attempt) * 2 due to current driver bug = expected 20s execution time + // (0s attempt + 0s attempt + 10s wait + 0s attempt) = expected 10s execution time try { - testConnectionRetry("blah", "retryConn={4060};"); + testConnectionRetry("blah", "retryConn={4060,4070};connectRetryCount=2;connectRetryInterval=10"); } catch (Exception e) { assertTrue( (e.getMessage().toLowerCase() @@ -557,7 +557,7 @@ public void connectionTimingTest() { // Append should work the same way try { - testConnectionRetry("blah", "retryConn={+4060,4070};"); + testConnectionRetry("blah", "retryConn={+4060,4070};connectRetryCount=2;connectRetryInterval=10"); } catch (Exception e) { assertTrue( (e.getMessage().toLowerCase()