From 896c4fc6b63ed9e0c526ff127c8dfaa9faa4fd45 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 23 Aug 2021 10:41:47 -0700 Subject: [PATCH] Include 42108 and 42109 to retriable transient errors list (#1215) * Include 42108 and 42109 to retriable transient errors list * Update tests --- .../SqlClient/SqlInternalConnectionTds.cs | 8 +- .../SqlClient/SqlInternalConnectionTds.cs | 8 +- .../SqlConfigurableRetryFactory.cs | 2 + .../SqlConfigurableRetryLogicManager.cs | 1 - .../SqlConnectionBasicTests.cs | 104 ++++++++---------- .../SQL/RetryLogic/RetryLogicConfigHelper.cs | 4 +- .../SQL/RetryLogic/RetryLogicTestHelper.cs | 2 + .../TDS.Servers/TransientFaultTDSServer.cs | 4 + 8 files changed, 70 insertions(+), 63 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index d506f9ba11..19bd5188be 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -269,7 +269,13 @@ internal bool IsDNSCachingBeforeRedirectSupported // Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later. // If the problem persists, contact customer support, and provide them the session tracing ID of '%.*ls'. - 40613 + 40613, + + // Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. + 42108, + + // The SQL pool is warming up. Please try again. + 42109 }; internal SessionData CurrentSessionData diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index b9b0a217e4..3f5c32b118 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -584,7 +584,7 @@ internal SqlInternalConnectionTds( SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, constructed new TDS internal connection", ObjectID); } - // The erros in the transient error set are contained in + // The errors in the transient error set are contained in // https://azure.microsoft.com/en-us/documentation/articles/sql-database-develop-error-messages/#transient-faults-connection-loss-and-other-temporary-errors private static void populateTransientErrors() { @@ -614,6 +614,12 @@ private static void populateTransientErrors() // Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later. // If the problem persists, contact customer support, and provide them the session tracing ID of '%.*ls'. transientErrors.Add(40613); + + // Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. + transientErrors.Add(42108); + + // The SQL pool is warming up. Please try again. + transientErrors.Add(42109); // Do federation errors deserve to be here ? // Note: Federation errors 10053 and 10054 might also deserve inclusion in your retry logic. //transientErrors.Add(10053); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs index 194a8aef41..63fd370901 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryFactory.cs @@ -54,6 +54,8 @@ private static readonly HashSet s_defaultTransientErrors 40501, // The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d. 40540, // The service has encountered an error processing your request. Please try again. 40197, // The service has encountered an error processing your request. Please try again. Error code %d. + 42108, // Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. + 42109, // The SQL pool is warming up. Please try again. 10929, // Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d. However, the server is currently too busy to support requests greater than %d for this database. For more information, see http://go.microsoft.com/fwlink/?LinkId=267637. Otherwise, please try again later. 10928, // Resource ID: %d. The %s limit for the database is %d and has been reached. For more information, see http://go.microsoft.com/fwlink/?LinkId=267637. 10060, // An error has occurred while establishing a connection to the server. When connecting to SQL Server, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: TCP Provider, error: 0 - A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.) (Microsoft SQL Server, Error: 10060) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.cs index cac4c04645..7871e6522f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.cs @@ -85,7 +85,6 @@ internal static SqlRetryLogicBaseProvider CommandProvider } } } - } internal interface IAppContextSwitchOverridesSection diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index edd24a7c53..0ec7a41973 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -17,13 +17,9 @@ public class SqlConnectionBasicTests [Fact] public void ConnectionTest() { - using (TestTdsServer server = TestTdsServer.StartTestServer()) - { - using (SqlConnection connection = new SqlConnection(server.ConnectionString)) - { - connection.Open(); - } - } + using TestTdsServer server = TestTdsServer.StartTestServer(); + using SqlConnection connection = new SqlConnection(server.ConnectionString); + connection.Open(); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] @@ -31,53 +27,48 @@ public void ConnectionTest() [PlatformSpecific(TestPlatforms.Windows)] public void IntegratedAuthConnectionTest() { - using (TestTdsServer server = TestTdsServer.StartTestServer()) - { - SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(server.ConnectionString); - builder.IntegratedSecurity = true; - using (SqlConnection connection = new SqlConnection(builder.ConnectionString)) - { - connection.Open(); - } - } + using TestTdsServer server = TestTdsServer.StartTestServer(); + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(server.ConnectionString); + builder.IntegratedSecurity = true; + using SqlConnection connection = new SqlConnection(builder.ConnectionString); + connection.Open(); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))] + [InlineData(40613)] + [InlineData(42108)] + [InlineData(42109)] [PlatformSpecific(TestPlatforms.Windows)] - public void TransientFaultTest() + public void TransientFaultTest(uint errorCode) { - using (TransientFaultTDSServer server = TransientFaultTDSServer.StartTestServer(true, true, 40613)) + using TransientFaultTDSServer server = TransientFaultTDSServer.StartTestServer(true, true, errorCode); + SqlConnectionStringBuilder builder = new() { - SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder() - { - DataSource = "localhost," + server.Port, - IntegratedSecurity = true, - Encrypt = false - }; + DataSource = "localhost," + server.Port, + IntegratedSecurity = true, + Encrypt = false + }; - using (SqlConnection connection = new SqlConnection(builder.ConnectionString)) + using SqlConnection connection = new(builder.ConnectionString); + try + { + connection.Open(); + Assert.Equal(ConnectionState.Open, connection.State); + } + catch (Exception e) + { + if (null != connection) { - try - { - connection.Open(); - Assert.Equal(ConnectionState.Open, connection.State); - } - catch (Exception e) - { - if (null != connection) - { - Assert.Equal(ConnectionState.Closed, connection.State); - } - Assert.False(true, e.Message); - } + Assert.Equal(ConnectionState.Closed, connection.State); } + Assert.False(true, e.Message); } } [Fact] public void SqlConnectionDbProviderFactoryTest() { - SqlConnection con = new SqlConnection(); + SqlConnection con = new(); PropertyInfo dbProviderFactoryProperty = con.GetType().GetProperty("DbProviderFactory", BindingFlags.NonPublic | BindingFlags.Instance); Assert.NotNull(dbProviderFactoryProperty); DbProviderFactory factory = dbProviderFactoryProperty.GetValue(con) as DbProviderFactory; @@ -104,23 +95,22 @@ public void SqlConnectionEmptyParameters() Assert.False(new SqlConnectionStringBuilder(con.ConnectionString).IntegratedSecurity); } - [Fact] - public void SqlConnectionInvalidParameters() + [Theory] + [InlineData("Timeout=null;")] + [InlineData("Timeout= null;")] + [InlineData("Timeout=1 1;")] + [InlineData("Timeout=1a;")] + [InlineData("Integrated Security=truee")] + public void SqlConnectionInvalidParameters(string connString) { - Assert.Throws(() => new SqlConnection("Timeout=null;")); - Assert.Throws(() => new SqlConnection("Timeout= null;")); - Assert.Throws(() => new SqlConnection("Timeout=1 1;")); - Assert.Throws(() => new SqlConnection("Timeout=1a;")); - Assert.Throws(() => new SqlConnection("Integrated Security=truee")); + Assert.Throws(() => new SqlConnection(connString)); } [Fact] public void ClosedConnectionSchemaRetrieval() { - using (SqlConnection connection = new SqlConnection(string.Empty)) - { - Assert.Throws(() => connection.GetSchema()); - } + using SqlConnection connection = new(string.Empty); + Assert.Throws(() => connection.GetSchema()); } [Theory] @@ -131,7 +121,7 @@ public void ClosedConnectionSchemaRetrieval() public void RetrieveWorkstationId(string workstation, bool withDispose, bool shouldMatchSetWorkstationId) { string connectionString = $"Workstation Id={workstation}"; - SqlConnection conn = new SqlConnection(connectionString); + SqlConnection conn = new(connectionString); if (withDispose) { conn.Dispose(); @@ -144,14 +134,12 @@ public void RetrieveWorkstationId(string workstation, bool withDispose, bool sho [Fact] public void ExceptionsWithMinPoolSizeCanBeHandled() { - string connectionString = $"Data Source={Guid.NewGuid().ToString()};uid=random;pwd=asd;Connect Timeout=2; Min Pool Size=3"; + string connectionString = $"Data Source={Guid.NewGuid()};uid=random;pwd=asd;Connect Timeout=2; Min Pool Size=3"; for (int i = 0; i < 2; i++) { - using (SqlConnection connection = new SqlConnection(connectionString)) - { - Exception exception = Record.Exception(() => connection.Open()); - Assert.True(exception is InvalidOperationException || exception is SqlException, $"Unexpected exception: {exception}"); - } + using SqlConnection connection = new(connectionString); + Exception exception = Record.Exception(() => connection.Open()); + Assert.True(exception is InvalidOperationException || exception is SqlException, $"Unexpected exception: {exception}"); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs index f1ad1c98f8..07714d49f9 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs @@ -28,7 +28,7 @@ public class RetryLogicConfigHelper private const string SqlRetryLogicTypeName = "Microsoft.Data.SqlClient.SqlRetryLogic"; - public const string DefaultTansientErrors = "1204, 1205, 1222, 49918, 49919, 49920, 4060, 4221, 40143, 40613, 40501, 40540, 40197, 10929, 10928, 10060, 10054, 10053, 997, 233, 64, 20, 0, -2, 207, 102, 2812"; + public const string DefaultTransientErrors = "1204, 1205, 1222, 49918, 49919, 49920, 4060, 4221, 40143, 40613, 40501, 40540, 40197, 42108, 42109, 10929, 10928, 10060, 10054, 10053, 997, 233, 64, 20, 0, -2, 207, 102, 2812"; private static readonly Random s_random = new Random(); @@ -151,7 +151,7 @@ public static object ReturnLoaderAndProviders(RetryLogicConfigs cnnCfg, RetryLog return loaderObj; } - public static RetryLogicConfigs CreateRandomConfig(string method, string authorizedSqlCondition = null, string transientErrors = DefaultTansientErrors) + public static RetryLogicConfigs CreateRandomConfig(string method, string authorizedSqlCondition = null, string transientErrors = DefaultTransientErrors) { TimeSpan start = TimeSpan.Zero; TimeSpan end = TimeSpan.FromSeconds(60); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs index 40905a3bc0..3a943461ee 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs @@ -61,6 +61,8 @@ private static readonly HashSet s_defaultTransientErrors 49920, // Cannot process request. Too many operations in progress for subscription "%ld". 4060, // Cannot open database "%.*ls" requested by the login. The login failed. 4221, // Login to read-secondary failed due to long wait on 'HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING'. The replica is not available for login because row versions are missing for transactions that were in-flight when the replica was recycled. The issue can be resolved by rolling back or committing the active transactions on the primary replica. Occurrences of this condition can be minimized by avoiding long write transactions on the primary. + 42108, // Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. + 42109, // The SQL pool is warming up. Please try again. 40143, // The service has encountered an error processing your request. Please try again. 40613, // Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later. If the problem persists, contact customer support, and provide them the session tracing ID of '%.*ls'. 40501, // The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d. diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs index 9bec7fdb34..419f7e5d24 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientFaultTDSServer.cs @@ -54,6 +54,10 @@ private static string GetErrorMessage(uint errorNumber) case 40613: return "Database on server is not currently available. Please retry the connection later. " + "If the problem persists, contact customer support, and provide them the session tracing ID."; + case 42108: + return "Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again."; + case 42109: + return "The SQL pool is warming up. Please try again."; } return "Unknown server error occurred"; }