@@ -230,6 +230,52 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial
230
230
/** Engine Edition 11 = Azure Synapse serverless SQL pool */
231
231
private static final int ENGINE_EDITION_SQL_AZURE_SYNAPSE_SERVERLESS_SQL_POOL = 11 ;
232
232
233
+ /**
234
+ * Azure SQL server endpoints
235
+ */
236
+ enum AzureSQLServerEndpoints {
237
+ AZURE_GENERIC_ENDPOINT (".database.windows.net" ),
238
+ AZURE_GERMAN_ENDPOINT (".database.cloudapi.de" ),
239
+ AZURE_USGOV_ENDPOINT (".database.usgovcloudapi.net" ),
240
+ AZURE_CHINA_ENDPOINT (".database.china.cloudapi.cn" );
241
+
242
+ private static final String ON_DEMAND_PREFIX = "-ondemand" ;
243
+ private static final String AZURE_SYNAPSE = "-ondemand.sql.azuresynapse." ;
244
+
245
+ private final String endpoint ;
246
+
247
+ private AzureSQLServerEndpoints (String endpoint ) {
248
+ this .endpoint = endpoint ;
249
+ }
250
+
251
+ static boolean isAzureSqlServerEndpoint (String endpoint ) {
252
+ for (AzureSQLServerEndpoints e : AzureSQLServerEndpoints .values ()) {
253
+ if (endpoint .endsWith (e .toString ())) {
254
+ return true ;
255
+ }
256
+ }
257
+ return false ;
258
+ }
259
+
260
+ static boolean isAzureSynapseOnDemandEndpoint (String endpoint ) {
261
+ if (endpoint .contains (AZURE_SYNAPSE )) {
262
+ return true ;
263
+ }
264
+
265
+ for (AzureSQLServerEndpoints e : AzureSQLServerEndpoints .values ()) {
266
+ if (endpoint .endsWith (ON_DEMAND_PREFIX + e .toString ())) {
267
+ return true ;
268
+ }
269
+ }
270
+ return false ;
271
+ }
272
+
273
+ @ Override
274
+ public String toString () {
275
+ return endpoint ;
276
+ }
277
+ }
278
+
233
279
/** flag indicating whether server is Azure */
234
280
private Boolean isAzure = null ;
235
281
@@ -665,6 +711,13 @@ private enum State {
665
711
*/
666
712
private final static int INTERMITTENT_TLS_MAX_RETRY = 5 ;
667
713
714
+ /**
715
+ * Defaults for Azure SQL Server retry counts
716
+ *
717
+ */
718
+ private final static int AZURE_SERVER_ENDPOINT_RETRY_COUNT_DEFAULT = 2 ;
719
+ private final static int AZURE_SYNAPSE_ONDEMAND_ENDPOINT_RETRY_COUNT_DEFAFULT = 5 ;
720
+
668
721
/** Indicates if we received a routing ENVCHANGE in the current connection attempt */
669
722
private boolean isRoutedInCurrentAttempt = false ;
670
723
@@ -997,7 +1050,8 @@ public void setIgnoreOffsetOnDateTimeOffsetConversion(boolean ignoreOffsetOnDate
997
1050
this .ignoreOffsetOnDateTimeOffsetConversion = ignoreOffsetOnDateTimeOffsetConversion ;
998
1051
}
999
1052
1000
- private boolean calcBigDecimalPrecision = SQLServerDriverBooleanProperty .CALC_BIG_DECIMAL_PRECISION .getDefaultValue ();
1053
+ private boolean calcBigDecimalPrecision = SQLServerDriverBooleanProperty .CALC_BIG_DECIMAL_PRECISION
1054
+ .getDefaultValue ();
1001
1055
1002
1056
@ Override
1003
1057
public boolean getCalcBigDecimalPrecision () {
@@ -1838,6 +1892,21 @@ void validateMaxSQLLoginName(String propName, String propValue) throws SQLServer
1838
1892
}
1839
1893
}
1840
1894
1895
+ /**
1896
+ * sleep for ms interval
1897
+ *
1898
+ * @param interval
1899
+ * in ms
1900
+ */
1901
+ private void sleepForInterval (long interval ) {
1902
+ try {
1903
+ Thread .sleep (interval );
1904
+ } catch (InterruptedException e ) {
1905
+ // re-interrupt the current thread, in order to restore the thread's interrupt status.
1906
+ Thread .currentThread ().interrupt ();
1907
+ }
1908
+ }
1909
+
1841
1910
Connection connect (Properties propsIn , SQLServerPooledConnection pooledConnection ) throws SQLServerException {
1842
1911
int loginTimeoutSeconds = SQLServerDriverIntProperty .LOGIN_TIMEOUT .getDefaultValue ();
1843
1912
if (propsIn != null ) {
@@ -1936,12 +2005,8 @@ Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnectio
1936
2005
+ sqlServerError .getErrorNumber () + ". Wait for connectRetryInterval("
1937
2006
+ connectRetryInterval + ")s before retry." );
1938
2007
}
1939
- try {
1940
- Thread .sleep (TimeUnit .SECONDS .toMillis (connectRetryInterval ));
1941
- } catch (InterruptedException ex ) {
1942
- // re-interrupt the current thread, in order to restore the thread's interrupt status.
1943
- Thread .currentThread ().interrupt ();
1944
- }
2008
+
2009
+ sleepForInterval (TimeUnit .SECONDS .toMillis (connectRetryInterval ));
1945
2010
}
1946
2011
}
1947
2012
}
@@ -2045,6 +2110,63 @@ int validateTimeout(SQLServerDriverIntProperty property) throws SQLServerExcepti
2045
2110
return timeout ;
2046
2111
}
2047
2112
2113
+ // Helper to validate connection retry properties
2114
+ void validateConnectionRetry () throws SQLServerException {
2115
+ // validate retry count
2116
+ connectRetryCount = SQLServerDriverIntProperty .CONNECT_RETRY_COUNT .getDefaultValue ();
2117
+ String sPropValue = activeConnectionProperties
2118
+ .getProperty (SQLServerDriverIntProperty .CONNECT_RETRY_COUNT .toString ());
2119
+ if (null != sPropValue && sPropValue .length () > 0 ) {
2120
+ try {
2121
+ connectRetryCount = Integer .parseInt (sPropValue );
2122
+ if (!SQLServerDriverIntProperty .CONNECT_RETRY_COUNT .isValidValue (connectRetryCount )) {
2123
+ MessageFormat form = new MessageFormat (
2124
+ SQLServerException .getErrString ("R_invalidConnectRetryCount" ));
2125
+ Object [] msgArgs = {sPropValue };
2126
+ SQLServerException .makeFromDriverError (this , this , form .format (msgArgs ), null , false );
2127
+ }
2128
+
2129
+ } catch (NumberFormatException e ) {
2130
+ MessageFormat form = new MessageFormat (SQLServerException .getErrString ("R_invalidConnectRetryCount" ));
2131
+ Object [] msgArgs = {sPropValue };
2132
+ SQLServerException .makeFromDriverError (this , this , form .format (msgArgs ), null , false );
2133
+ }
2134
+ } else {
2135
+ // if property was not set, detect and increase for Azure endpoints
2136
+ if (connectRetryCount == 1 ) {
2137
+
2138
+ // Set to larger default value for Azure connections to greatly improve recovery
2139
+ if (isAzureSynapseOnDemandEndpoint ()) {
2140
+ connectRetryCount = AZURE_SERVER_ENDPOINT_RETRY_COUNT_DEFAULT ;
2141
+ } else if (isAzureSqlServerEndpoint ()) {
2142
+ connectRetryCount = AZURE_SYNAPSE_ONDEMAND_ENDPOINT_RETRY_COUNT_DEFAFULT ;
2143
+ }
2144
+ }
2145
+ }
2146
+
2147
+ // validate retry interval
2148
+ connectRetryInterval = SQLServerDriverIntProperty .CONNECT_RETRY_INTERVAL .getDefaultValue ();
2149
+ sPropValue = activeConnectionProperties
2150
+ .getProperty (SQLServerDriverIntProperty .CONNECT_RETRY_INTERVAL .toString ());
2151
+ if (null != sPropValue && sPropValue .length () > 0 ) {
2152
+ try {
2153
+ connectRetryInterval = Integer .parseInt (sPropValue );
2154
+ if (!SQLServerDriverIntProperty .CONNECT_RETRY_INTERVAL .isValidValue (connectRetryInterval )) {
2155
+ MessageFormat form = new MessageFormat (
2156
+ SQLServerException .getErrString ("R_invalidConnectRetryInterval" ));
2157
+ Object [] msgArgs = {sPropValue };
2158
+ SQLServerException .makeFromDriverError (this , this , form .format (msgArgs ), null , false );
2159
+ }
2160
+
2161
+ } catch (NumberFormatException e ) {
2162
+ MessageFormat form = new MessageFormat (
2163
+ SQLServerException .getErrString ("R_invalidConnectRetryInterval" ));
2164
+ Object [] msgArgs = {sPropValue };
2165
+ SQLServerException .makeFromDriverError (this , this , form .format (msgArgs ), null , false );
2166
+ }
2167
+ }
2168
+ }
2169
+
2048
2170
/**
2049
2171
* Establish a physical database connection based on the user specified connection properties. Logon to the
2050
2172
* database.
@@ -3045,46 +3167,7 @@ else if (0 == requestedPacketSize)
3045
3167
3046
3168
String mirror = (null == fo ) ? failOverPartnerPropertyValue : null ;
3047
3169
3048
- connectRetryCount = SQLServerDriverIntProperty .CONNECT_RETRY_COUNT .getDefaultValue ();
3049
- sPropValue = activeConnectionProperties
3050
- .getProperty (SQLServerDriverIntProperty .CONNECT_RETRY_COUNT .toString ());
3051
- if (null != sPropValue && sPropValue .length () > 0 ) {
3052
- try {
3053
- connectRetryCount = Integer .parseInt (sPropValue );
3054
- } catch (NumberFormatException e ) {
3055
- MessageFormat form = new MessageFormat (
3056
- SQLServerException .getErrString ("R_invalidConnectRetryCount" ));
3057
- Object [] msgArgs = {sPropValue };
3058
- SQLServerException .makeFromDriverError (this , this , form .format (msgArgs ), null , false );
3059
- }
3060
- if (connectRetryCount < 0 || connectRetryCount > 255 ) {
3061
- MessageFormat form = new MessageFormat (
3062
- SQLServerException .getErrString ("R_invalidConnectRetryCount" ));
3063
- Object [] msgArgs = {sPropValue };
3064
- SQLServerException .makeFromDriverError (this , this , form .format (msgArgs ), null , false );
3065
- }
3066
- }
3067
-
3068
- connectRetryInterval = SQLServerDriverIntProperty .CONNECT_RETRY_INTERVAL .getDefaultValue ();
3069
- sPropValue = activeConnectionProperties
3070
- .getProperty (SQLServerDriverIntProperty .CONNECT_RETRY_INTERVAL .toString ());
3071
- if (null != sPropValue && sPropValue .length () > 0 ) {
3072
- try {
3073
- connectRetryInterval = Integer .parseInt (sPropValue );
3074
- } catch (NumberFormatException e ) {
3075
- MessageFormat form = new MessageFormat (
3076
- SQLServerException .getErrString ("R_invalidConnectRetryInterval" ));
3077
- Object [] msgArgs = {sPropValue };
3078
- SQLServerException .makeFromDriverError (this , this , form .format (msgArgs ), null , false );
3079
- }
3080
-
3081
- if (connectRetryInterval < 1 || connectRetryInterval > 60 ) {
3082
- MessageFormat form = new MessageFormat (
3083
- SQLServerException .getErrString ("R_invalidConnectRetryInterval" ));
3084
- Object [] msgArgs = {sPropValue };
3085
- SQLServerException .makeFromDriverError (this , this , form .format (msgArgs ), null , false );
3086
- }
3087
- }
3170
+ validateConnectionRetry ();
3088
3171
3089
3172
long startTime = System .currentTimeMillis ();
3090
3173
sessionRecovery .setLoginParameters (instanceValue , nPort , fo ,
@@ -3148,7 +3231,7 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu
3148
3231
3149
3232
final boolean isDBMirroring = null != mirror || null != foActual ;
3150
3233
3151
- int sleepInterval = BACKOFF_INTERVAL ; // milliseconds to sleep (back off) between attempts.
3234
+ int fedauthRetryInterval = BACKOFF_INTERVAL ; // milliseconds to sleep (back off) between attempts.
3152
3235
3153
3236
long timeoutUnitInterval ;
3154
3237
@@ -3407,7 +3490,7 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu
3407
3490
// Check sleep interval to make sure we won't exceed the timeout
3408
3491
// Do this in the catch block so we can re-throw the current exception
3409
3492
long remainingMilliseconds = timerRemaining (timerExpire );
3410
- if (remainingMilliseconds <= sleepInterval ) {
3493
+ if (remainingMilliseconds <= fedauthRetryInterval ) {
3411
3494
3412
3495
if (loggerResiliency .isLoggable (Level .FINER )) {
3413
3496
loggerResiliency .finer (toString () + " Connection open - connection failed on attempt: "
@@ -3441,13 +3524,9 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu
3441
3524
+ ". Wait for connectRetryInterval(" + connectRetryInterval + ")s before retry #"
3442
3525
+ attemptNumber );
3443
3526
}
3444
- try {
3445
- Thread .sleep (sleepInterval );
3446
- } catch (InterruptedException e ) {
3447
- // re-interrupt the current thread, in order to restore the thread's interrupt status.
3448
- Thread .currentThread ().interrupt ();
3449
- }
3450
- sleepInterval = (sleepInterval < 500 ) ? sleepInterval * 2 : 1000 ;
3527
+
3528
+ sleepForInterval (fedauthRetryInterval );
3529
+ fedauthRetryInterval = (fedauthRetryInterval < 500 ) ? fedauthRetryInterval * 2 : 1000 ;
3451
3530
}
3452
3531
3453
3532
// Update timeout interval (but no more than the point where we're supposed to fail: timerExpire)
@@ -5896,7 +5975,7 @@ private SqlAuthenticationToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throw
5896
5975
5897
5976
String user = activeConnectionProperties .getProperty (SQLServerDriverStringProperty .USER .toString ());
5898
5977
5899
- // No: of milliseconds to sleep for the initial back off.
5978
+ // No of milliseconds to sleep for the initial back off.
5900
5979
int sleepInterval = BACKOFF_INTERVAL ;
5901
5980
5902
5981
if (!msalContextExists ()
@@ -6025,12 +6104,7 @@ private SqlAuthenticationToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throw
6025
6104
+ millisecondsRemaining + " milliseconds." );
6026
6105
}
6027
6106
6028
- try {
6029
- Thread .sleep (sleepInterval );
6030
- } catch (InterruptedException e1 ) {
6031
- // re-interrupt the current thread, in order to restore the thread's interrupt status.
6032
- Thread .currentThread ().interrupt ();
6033
- }
6107
+ sleepForInterval (sleepInterval );
6034
6108
sleepInterval = sleepInterval * 2 ;
6035
6109
}
6036
6110
}
@@ -8286,6 +8360,34 @@ boolean isAzureMI() {
8286
8360
return isAzureMI ;
8287
8361
}
8288
8362
8363
+ boolean isAzureSqlServerEndpoint () {
8364
+ String serverName = activeConnectionProperties
8365
+ .getProperty (SQLServerDriverStringProperty .SERVER_NAME .toString ());
8366
+ if (null != serverName && serverName .length () > 0 ) {
8367
+ // serverName without named instance
8368
+ int px = serverName .indexOf ('\\' );
8369
+ String parsedServerName = (px >= 0 ) ? serverName .substring (0 , px ) : serverName ;
8370
+
8371
+ return AzureSQLServerEndpoints .isAzureSqlServerEndpoint (parsedServerName );
8372
+ }
8373
+
8374
+ return false ;
8375
+ }
8376
+
8377
+ boolean isAzureSynapseOnDemandEndpoint () {
8378
+ String serverName = activeConnectionProperties
8379
+ .getProperty (SQLServerDriverStringProperty .SERVER_NAME .toString ());
8380
+ if (null != serverName && serverName .length () > 0 ) {
8381
+ // serverName without named instance
8382
+ int px = serverName .indexOf ('\\' );
8383
+ String parsedServerName = (px >= 0 ) ? serverName .substring (0 , px ) : serverName ;
8384
+
8385
+ return AzureSQLServerEndpoints .isAzureSqlServerEndpoint (parsedServerName );
8386
+ }
8387
+
8388
+ return false ;
8389
+ }
8390
+
8289
8391
/**
8290
8392
* Checks if the connection established to server supports transactions.
8291
8393
*
0 commit comments