Skip to content

Commit 38564d9

Browse files
Configurable Retry Logic II - Connection Retry (#2519)
* Added back changes for connection part of CRL, which only affect exis 0296179 ting files. To allow for PR to be created. * added more * Added tests * Add another test; see if I can still push to GitHub * Added more * Decoupled statement and connection parts to ensure tests pass. * Cleanup. * Changed tests to account for new changes to retry logic.
1 parent ef6b770 commit 38564d9

File tree

11 files changed

+361
-28
lines changed

11 files changed

+361
-28
lines changed

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

Lines changed: 99 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public class ConfigurableRetryLogic {
3939
private static final String FORWARD_SLASH = "/";
4040
private static final String EQUALS_SIGN = "=";
4141
private static final String RETRY_EXEC = "retryExec";
42+
private static final String RETRY_CONN = "retryConn";
43+
private static final String STATEMENT = "statement";
44+
private static boolean replaceFlag = false; // Are we replacing the list of transient errors?
4245
/**
4346
* The time the properties file was last modified.
4447
*/
@@ -52,14 +55,23 @@ public class ConfigurableRetryLogic {
5255
*/
5356
private static final AtomicReference<String> lastQuery = new AtomicReference<>("");
5457
/**
55-
* The previously read rules from the connection string.
58+
* The previously read statement rules from the connection string.
5659
*/
57-
private static final AtomicReference<String> prevRulesFromConnectionString = new AtomicReference<>("");
60+
private static final AtomicReference<String> prevStmtRulesFromConnString = new AtomicReference<>("");
61+
/**
62+
* The previously read connection rules from the connection string.
63+
*/
64+
private static final AtomicReference<String> prevConnRulesFromConnString = new AtomicReference<>("");
5865
/**
5966
* The list of statement retry rules.
6067
*/
6168
private static final AtomicReference<HashMap<Integer, ConfigurableRetryRule>> stmtRules = new AtomicReference<>(
6269
new HashMap<>());
70+
/**
71+
* The list of connection retry rules.
72+
*/
73+
private static final AtomicReference<HashMap<Integer, ConfigurableRetryRule>> connRules = new AtomicReference<>(
74+
new HashMap<>());
6375
private static ConfigurableRetryLogic singleInstance;
6476

6577
/**
@@ -70,7 +82,8 @@ public class ConfigurableRetryLogic {
7082
*/
7183
private ConfigurableRetryLogic() throws SQLServerException {
7284
timeLastRead.compareAndSet(0, new Date().getTime());
73-
setUpRules(null);
85+
setUpStatementRules(null);
86+
setUpConnectionRules(null);
7487
}
7588

7689
/**
@@ -102,7 +115,8 @@ public static ConfigurableRetryLogic getInstance() throws SQLServerException {
102115

103116
/**
104117
* If it has been INTERVAL_BETWEEN_READS_IN_MS (30 secs) since last read, see if we last did a file read, if so
105-
* only reread if the file has been modified. If no file read, set up rules using the prev. connection string rules.
118+
* only reread if the file has been modified. If no file read, set up rules using the previous connection
119+
* string (statement and connection) rules
106120
*
107121
* @throws SQLServerException
108122
* when an exception occurs
@@ -116,25 +130,40 @@ private static void refreshRuleSet() throws SQLServerException {
116130
// If timeLastModified is set, we previously read from file, so we setUpRules also reading from file
117131
File f = new File(getCurrentClassPath());
118132
if (f.lastModified() != timeLastModified.get()) {
119-
setUpRules(null);
133+
setUpStatementRules(null);
134+
setUpConnectionRules(null);
120135
}
121136
} else {
122-
setUpRules(prevRulesFromConnectionString.get());
137+
setUpStatementRules(prevStmtRulesFromConnString.get());
138+
setUpConnectionRules(prevConnRulesFromConnString.get());
123139
}
124140
}
125141
}
126142

127143
/**
128-
* Sets rules given from connection string.
144+
* Sets statement rules given from connection string.
145+
*
146+
* @param newRules
147+
* the new rules to use
148+
* @throws SQLServerException
149+
* when an exception occurs
150+
*/
151+
void setStatementRulesFromConnectionString(String newRules) throws SQLServerException {
152+
prevStmtRulesFromConnString.set(newRules);
153+
setUpStatementRules(prevStmtRulesFromConnString.get());
154+
}
155+
156+
/**
157+
* Sets connection rules given from connection string.
129158
*
130159
* @param newRules
131160
* the new rules to use
132161
* @throws SQLServerException
133162
* when an exception occurs
134163
*/
135-
void setFromConnectionString(String newRules) throws SQLServerException {
136-
prevRulesFromConnectionString.set(newRules);
137-
setUpRules(prevRulesFromConnectionString.get());
164+
void setConnectionRulesFromConnectionString(String newRules) throws SQLServerException {
165+
prevConnRulesFromConnString.set(newRules);
166+
setUpConnectionRules(prevConnRulesFromConnString.get());
138167
}
139168

140169
/**
@@ -164,19 +193,34 @@ String getLastQuery() {
164193
* @throws SQLServerException
165194
* if an exception occurs
166195
*/
167-
private static void setUpRules(String cxnStrRules) throws SQLServerException {
196+
private static void setUpStatementRules(String cxnStrRules) throws SQLServerException {
168197
LinkedList<String> temp;
169198

170199
stmtRules.set(new HashMap<>());
171200
lastQuery.set("");
172201

173202
if (cxnStrRules == null || cxnStrRules.isEmpty()) {
174-
temp = readFromFile();
203+
temp = readFromFile(RETRY_EXEC);
175204
} else {
176205
temp = new LinkedList<>();
177206
Collections.addAll(temp, cxnStrRules.split(SEMI_COLON));
178207
}
179-
createRules(temp);
208+
createStatementRules(temp);
209+
}
210+
211+
private static void setUpConnectionRules(String cxnStrRules) throws SQLServerException {
212+
LinkedList<String> temp;
213+
214+
connRules.set(new HashMap<>());
215+
lastQuery.set("");
216+
217+
if (cxnStrRules == null || cxnStrRules.isEmpty()) {
218+
temp = readFromFile(RETRY_CONN);
219+
} else {
220+
temp = new LinkedList<>();
221+
Collections.addAll(temp, cxnStrRules.split(SEMI_COLON));
222+
}
223+
createConnectionRules(temp);
180224
}
181225

182226
/**
@@ -187,7 +231,7 @@ private static void setUpRules(String cxnStrRules) throws SQLServerException {
187231
* @throws SQLServerException
188232
* if unable to create rules from the inputted list
189233
*/
190-
private static void createRules(LinkedList<String> listOfRules) throws SQLServerException {
234+
private static void createStatementRules(LinkedList<String> listOfRules) throws SQLServerException {
191235
stmtRules.set(new HashMap<>());
192236

193237
for (String potentialRule : listOfRules) {
@@ -206,6 +250,29 @@ private static void createRules(LinkedList<String> listOfRules) throws SQLServer
206250
}
207251
}
208252

253+
private static void createConnectionRules(LinkedList<String> listOfRules) throws SQLServerException {
254+
connRules.set(new HashMap<>());
255+
replaceFlag = false;
256+
257+
for (String potentialRule : listOfRules) {
258+
ConfigurableRetryRule rule = new ConfigurableRetryRule(potentialRule);
259+
if (rule.replaceExisting) {
260+
replaceFlag = true;
261+
}
262+
263+
if (rule.getError().contains(COMMA)) {
264+
String[] arr = rule.getError().split(COMMA);
265+
266+
for (String retryError : arr) {
267+
ConfigurableRetryRule splitRule = new ConfigurableRetryRule(retryError, rule);
268+
connRules.get().put(Integer.parseInt(splitRule.getError()), splitRule);
269+
}
270+
} else {
271+
connRules.get().put(Integer.parseInt(rule.getError()), rule);
272+
}
273+
}
274+
}
275+
209276
/**
210277
* Gets the current class path (for use in file reading).
211278
*
@@ -241,7 +308,7 @@ private static String getCurrentClassPath() throws SQLServerException {
241308
* @throws SQLServerException
242309
* if unable to read from the file
243310
*/
244-
private static LinkedList<String> readFromFile() throws SQLServerException {
311+
private static LinkedList<String> readFromFile(String connectionStringProperty) throws SQLServerException {
245312
String filePath = getCurrentClassPath();
246313
LinkedList<String> list = new LinkedList<>();
247314

@@ -250,7 +317,7 @@ private static LinkedList<String> readFromFile() throws SQLServerException {
250317
try (BufferedReader buffer = new BufferedReader(new FileReader(f))) {
251318
String readLine;
252319
while ((readLine = buffer.readLine()) != null) {
253-
if (readLine.startsWith(RETRY_EXEC)) {
320+
if (readLine.startsWith(connectionStringProperty)) { // Either "retryExec" or "retryConn"
254321
String value = readLine.split(EQUALS_SIGN)[1];
255322
Collections.addAll(list, value.split(SEMI_COLON));
256323
}
@@ -280,13 +347,25 @@ private static LinkedList<String> readFromFile() throws SQLServerException {
280347
* @throws SQLServerException
281348
* when an exception occurs
282349
*/
283-
ConfigurableRetryRule searchRuleSet(int ruleToSearchFor) throws SQLServerException {
350+
ConfigurableRetryRule searchRuleSet(int ruleToSearchFor, String ruleSet) throws SQLServerException {
284351
refreshRuleSet();
285-
for (Map.Entry<Integer, ConfigurableRetryRule> entry : stmtRules.get().entrySet()) {
286-
if (entry.getKey() == ruleToSearchFor) {
287-
return entry.getValue();
352+
if (ruleSet.equals(STATEMENT)) {
353+
for (Map.Entry<Integer, ConfigurableRetryRule> entry : stmtRules.get().entrySet()) {
354+
if (entry.getKey() == ruleToSearchFor) {
355+
return entry.getValue();
356+
}
357+
}
358+
} else {
359+
for (Map.Entry<Integer, ConfigurableRetryRule> entry : connRules.get().entrySet()) {
360+
if (entry.getKey() == ruleToSearchFor) {
361+
return entry.getValue();
362+
}
288363
}
289364
}
290365
return null;
291366
}
367+
368+
boolean getReplaceFlag() {
369+
return replaceFlag;
370+
}
292371
}

src/main/java/com/microsoft/sqlserver/jdbc/ConfigurableRetryRule.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class ConfigurableRetryRule {
2525
private int retryCount = 1;
2626
private String retryQueries = "";
2727
private String retryError;
28+
boolean isConnection = false;
29+
boolean replaceExisting = false;
2830

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

@@ -70,6 +72,18 @@ private void copyFromRule(ConfigurableRetryRule baseRule) {
7072
this.retryCount = baseRule.retryCount;
7173
this.retryQueries = baseRule.retryQueries;
7274
this.waitTimes = baseRule.waitTimes;
75+
this.isConnection = baseRule.isConnection;
76+
}
77+
78+
private String appendOrReplace(String retryError) {
79+
if (retryError.startsWith(PLUS_SIGN)) {
80+
replaceExisting = false;
81+
StringUtils.isNumeric(retryError.substring(1));
82+
return retryError.substring(1);
83+
} else {
84+
replaceExisting = true;
85+
return retryError;
86+
}
7387
}
7488

7589
/**
@@ -152,7 +166,12 @@ private void checkParameter(String value) throws SQLServerException {
152166
* if a rule or parameter has invalid inputs
153167
*/
154168
private void addElements(String[] rule) throws SQLServerException {
155-
if (rule.length == 2 || rule.length == 3) {
169+
if (rule.length == 1) {
170+
String errorWithoutOptionalPrefix = appendOrReplace(rule[0]);
171+
checkParameter(errorWithoutOptionalPrefix);
172+
isConnection = true;
173+
retryError = errorWithoutOptionalPrefix;
174+
} else if (rule.length == 2 || rule.length == 3) {
156175
checkParameter(rule[0]);
157176
retryError = rule[0];
158177
String[] timings = rule[1].split(COMMA);

src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,22 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource {
13631363
*/
13641364
String getRetryExec();
13651365

1366+
/**
1367+
* Returns value of 'retryConn' from Connection String.
1368+
*
1369+
* @param retryConn
1370+
* Set of rules used for connection retry
1371+
*/
1372+
void setRetryConn(String retryConn);
1373+
1374+
/**
1375+
* Sets the value for 'retryConn' property
1376+
*
1377+
* @return retryConn
1378+
* String value
1379+
*/
1380+
String getRetryConn();
1381+
13661382
/**
13671383
* useFlexibleCallableStatements is temporarily removed. This is meant as a no-op.
13681384
*

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

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,28 @@ public void setRetryExec(String retryExec) {
10891089
this.retryExec = retryExec;
10901090
}
10911091

1092+
private String retryConn = SQLServerDriverStringProperty.RETRY_CONN.getDefaultValue();
1093+
1094+
/**
1095+
* Returns the set of configurable connection retry rules set in retryConn
1096+
*
1097+
* @return
1098+
* A string containing statement retry rules.
1099+
*/
1100+
public String getRetryConn() {
1101+
return retryConn;
1102+
}
1103+
1104+
/**
1105+
* Sets the list of configurable connection retry rules, for the given connection, in retryConn.
1106+
*
1107+
* @param retryConn
1108+
* The list of retry rules to set, as a string.
1109+
*/
1110+
public void setRetryConn(String retryConn) {
1111+
this.retryConn = retryConn;
1112+
}
1113+
10921114
/** Session Recovery Object */
10931115
private transient IdleConnectionResiliency sessionRecovery = new IdleConnectionResiliency(this);
10941116

@@ -2039,10 +2061,23 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio
20392061
}
20402062
throw e;
20412063
} else {
2042-
// only retry if transient error
2064+
// Only retry if matches configured CRL rules, or transient error (if CRL is not in use)
20432065
SQLServerError sqlServerError = e.getSQLServerError();
2044-
if (!TransientError.isTransientError(sqlServerError)) {
2066+
if (null == sqlServerError) {
20452067
throw e;
2068+
} else {
2069+
ConfigurableRetryRule rule = ConfigurableRetryLogic.getInstance()
2070+
.searchRuleSet(sqlServerError.getErrorNumber(), "connection");
2071+
2072+
if (null == rule) {
2073+
if (ConfigurableRetryLogic.getInstance().getReplaceFlag()) {
2074+
throw e;
2075+
} else {
2076+
if (!TransientError.isTransientError(sqlServerError)) {
2077+
throw e;
2078+
}
2079+
}
2080+
}
20462081
}
20472082

20482083
// check if there's time to retry, no point to wait if no time left
@@ -2385,7 +2420,16 @@ Connection connectInternal(Properties propsIn,
23852420
activeConnectionProperties.setProperty(sPropKey, sPropValue);
23862421
}
23872422
retryExec = sPropValue;
2388-
ConfigurableRetryLogic.getInstance().setFromConnectionString(sPropValue);
2423+
ConfigurableRetryLogic.getInstance().setStatementRulesFromConnectionString(sPropValue);
2424+
2425+
sPropKey = SQLServerDriverStringProperty.RETRY_CONN.toString();
2426+
sPropValue = activeConnectionProperties.getProperty(sPropKey);
2427+
if (null == sPropValue) {
2428+
sPropValue = SQLServerDriverStringProperty.RETRY_CONN.getDefaultValue();
2429+
activeConnectionProperties.setProperty(sPropKey, sPropValue);
2430+
}
2431+
retryConn = sPropValue;
2432+
ConfigurableRetryLogic.getInstance().setConnectionRulesFromConnectionString(sPropValue);
23892433

23902434
sPropKey = SQLServerDriverBooleanProperty.CALC_BIG_DECIMAL_PRECISION.toString();
23912435
sPropValue = activeConnectionProperties.getProperty(sPropKey);

0 commit comments

Comments
 (0)