Skip to content

Commit

Permalink
added more
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeffery-Wasty committed Sep 24, 2024
1 parent ea397d0 commit bd5c8eb
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 29 deletions.
106 changes: 82 additions & 24 deletions src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -52,14 +56,23 @@ public class ConfigurableRetryLogic {
*/
private static final AtomicReference<String> lastQuery = new AtomicReference<>("");
/**
* The previously read rules from the connection string.
* The previously read statement rules from the connection string.
*/
private static final AtomicReference<String> prevRulesFromConnectionString = new AtomicReference<>("");
private static final AtomicReference<String> prevStmtRulesFromConnString = new AtomicReference<>("");
/**
* The previously read connection rules from the connection string.
*/
private static final AtomicReference<String> prevConnRulesFromConnString = new AtomicReference<>("");
/**
* The list of statement retry rules.
*/
private static final AtomicReference<HashMap<Integer, ConfigurableRetryRule>> stmtRules = new AtomicReference<>(
new HashMap<>());
/**
* The list of connection retry rules.
*/
private static final AtomicReference<HashMap<Integer, ConfigurableRetryRule>> connRules = new AtomicReference<>(
new HashMap<>());
private static ConfigurableRetryLogic singleInstance;

/**
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
}
}
Expand All @@ -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);
}

/**
Expand All @@ -160,34 +179,42 @@ 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<String> 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);
}

/**
* Creates and stores rules based on the inputted list of rules.
*
* @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<String> listOfRules) throws SQLServerException {
private static void createRules(LinkedList<String> listOfRules, String ruleType) throws SQLServerException {
connRules.set(new HashMap<>());
stmtRules.set(new HashMap<>());

for (String potentialRule : listOfRules) {
Expand All @@ -198,10 +225,29 @@ private static void createRules(LinkedList<String> 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);

Check warning on line 235 in src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java#L235

Added line #L235 was not covered by tests
} 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);

Check warning on line 247 in src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java#L247

Added line #L247 was not covered by tests
} else {
stmtRules.get().put(Integer.parseInt(rule.getError()), rule);
}
}
}
}
Expand Down Expand Up @@ -241,7 +287,7 @@ private static String getCurrentClassPath() throws SQLServerException {
* @throws SQLServerException
* if unable to read from the file
*/
private static LinkedList<String> readFromFile() throws SQLServerException {
private static LinkedList<String> readFromFile(String connectionStringProperty) throws SQLServerException {
String filePath = getCurrentClassPath();
LinkedList<String> list = new LinkedList<>();

Expand All @@ -250,7 +296,7 @@ private static LinkedList<String> 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));
}
Expand Down Expand Up @@ -280,13 +326,25 @@ private static LinkedList<String> 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<Integer, ConfigurableRetryRule> entry : stmtRules.get().entrySet()) {
if (entry.getKey() == ruleToSearchFor) {
return entry.getValue();
if (ruleSet.equals(STATEMENT)) {
for (Map.Entry<Integer, ConfigurableRetryRule> entry : stmtRules.get().entrySet()) {
if (entry.getKey() == ruleToSearchFor) {
return entry.getValue();
}
}
} else {
for (Map.Entry<Integer, ConfigurableRetryRule> entry : connRules.get().entrySet()) {
if (entry.getKey() == ruleToSearchFor) {
return entry.getValue();

Check warning on line 340 in src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryLogic.java#L340

Added line #L340 was not covered by tests
}
}
}
return null;
}

boolean getReplaceFlag() {
return replaceFlag;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class ConfigurableRetryRule {
private int retryCount = 1;
private String retryQueries = "";
private String retryError;
boolean isConnection = false;
boolean replaceExisting = false;

private ArrayList<Integer> waitTimes = new ArrayList<>();

Expand Down Expand Up @@ -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);

Check warning on line 82 in src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java#L80-L82

Added lines #L80 - L82 were not covered by tests
} else {
replaceExisting = true;
return retryError;

Check warning on line 85 in src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java#L84-L85

Added lines #L84 - L85 were not covered by tests
}
}

/**
Expand Down Expand Up @@ -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;

Check warning on line 173 in src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java#L170-L173

Added lines #L170 - L173 were not covered by tests
} else if (rule.length == 2 || rule.length == 3) {
checkParameter(rule[0]);
retryError = rule[0];
String[] timings = rule[1].split(COMMA);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Check warning on line 1098 in src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java#L1098

Added line #L1098 was not covered by tests
}

/**
* 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;
}

Check warning on line 1109 in src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java#L1108-L1109

Added lines #L1108 - L1109 were not covered by tests

/** Session Recovery Object */
private transient IdleConnectionResiliency sessionRecovery = new IdleConnectionResiliency(this);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}."},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit bd5c8eb

Please sign in to comment.