diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs index 29ff71270c..b943e6afd3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs @@ -92,25 +92,22 @@ public static void SqlLocalDbSharedInstanceConnectionTest() #region NamedPipeTests - [Fact] - [ActiveIssue("20245")] //pending pipeline configuration + [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] public static void SqlLocalDbNamedPipeConnectionTest() { ConnectionTest(s_localDbNamedPipeConnectionString); } - [Fact] - [ActiveIssue("20245")] //pending pipeline configuration - public static void LocalDBNamedPipeEncryptionNotSupportedTest() + [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] + public static void LocalDbNamedPipeEncryptionNotSupportedTest() { // Encryption is not supported by SQL Local DB. // But connection should succeed as encryption is disabled by driver. ConnectionWithEncryptionTest(s_localDbNamedPipeConnectionString); } - [Fact] - [ActiveIssue("20245")] //pending pipeline configuration - public static void LocalDBNamepipeMarsTest() + [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] + public static void LocalDbNamedPipeMarsTest() { ConnectionWithMarsTest(s_localDbNamedPipeConnectionString); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs index 8b5b6d0a40..aaf45c97bc 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs @@ -53,34 +53,42 @@ public void TestMain() } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] - [ActiveIssue("5531")] - public void TestPacketNumberWraparound() - { - // this test uses a specifically crafted sql record enumerator and data to put the TdsParserStateObject.WritePacket(byte,bool) - // into a state where it can't differentiate between a packet in the middle of a large packet-set after a byte counter wraparound - // and the first packet of the connection and in doing so trips over a check for packet length from the input which has been - // forced to tell it that there is no output buffer space left, this causes an uncancellable infinite loop - - // if the enumerator is completely read to the end then the bug is no longer present and the packet creation task returns, - // if the timeout occurs it is probable (but not absolute) that the write is stuck - + public async Task TestPacketNumberWraparound() + { + // This test uses a specifically crafted SQL record enumerator and data to put the + // TdsParserStateObject.WritePacket(byte,bool) into a state where it can't + // differentiate between a packet in the middle of a large packet-set after a byte + // counter wraparound and the first packet of the connection and in doing so trips over + // a check for packet length from the input which has been forced to tell it that there + // is no output buffer space left, this causes an uncancellable infinite loop. + // + // If the enumerator is completely read to the end then the bug is no longer present + // and the packet creation task returns, if the timeout occurs it is probable (but not + // absolute) that the write operation is stuck. + + // Arrange var enumerator = new WraparoundRowEnumerator(1000000); + using var cancellationTokenSource = new CancellationTokenSource(); + // Act Stopwatch stopwatch = new(); stopwatch.Start(); - int returned = Task.WaitAny( - Task.Factory.StartNew( - () => RunPacketNumberWraparound(enumerator), - TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning - ), - Task.Delay(TimeSpan.FromSeconds(60)) - ); + + Task actionTask = Task.Factory.StartNew( + async () => await RunPacketNumberWraparound(enumerator, cancellationTokenSource.Token), + TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning); + Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(60), cancellationTokenSource.Token); + await Task.WhenAny(actionTask, timeoutTask); + stopwatch.Stop(); - if (enumerator.MaxCount != enumerator.Count) - { - Console.WriteLine($"enumerator.Count={enumerator.Count}, enumerator.MaxCount={enumerator.MaxCount}, elapsed={stopwatch.Elapsed.TotalSeconds}"); - } - Assert.True(enumerator.MaxCount == enumerator.Count); + cancellationTokenSource.Cancel(); + + // Assert + Assert.True( + enumerator.MaxCount == enumerator.Count, + $"enumerator.Count={enumerator.Count}, " + + $"enumerator.MaxCount={enumerator.MaxCount}, " + + $"elapsed={stopwatch.Elapsed}"); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] @@ -688,23 +696,27 @@ private void QueryHintsTest() } } - private static async Task RunPacketNumberWraparound(WraparoundRowEnumerator enumerator) + private static async Task RunPacketNumberWraparound( + WraparoundRowEnumerator enumerator, + CancellationToken cancellationToken) { using var connection = new SqlConnection(DataTestUtility.TCPConnectionString); - using var cmd = new SqlCommand("unimportant") - { - CommandType = CommandType.StoredProcedure, - Connection = connection, - }; - await cmd.Connection.OpenAsync(); - cmd.Parameters.Add(new SqlParameter("@rows", SqlDbType.Structured) + await connection.OpenAsync(cancellationToken); + + using var cmd = connection.CreateCommand(); + cmd.CommandType = CommandType.StoredProcedure; + cmd.CommandText = "unimportant"; + + var parameter = new SqlParameter("@rows", SqlDbType.Structured) { TypeName = "unimportant", - Value = enumerator, - }); + Value = enumerator + }; + cmd.Parameters.Add(parameter); + try { - await cmd.ExecuteNonQueryAsync(); + await cmd.ExecuteNonQueryAsync(cancellationToken); } catch (Exception) { 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 a9eb1b52a2..c9a64e5ef1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs @@ -7,41 +7,10 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; +using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { - /// Define the SQL command type by filtering purpose. - [Flags] - public enum FilterSqlStatements - { - /// Don't filter any SQL commands - None = 0, - /// Filter INSERT or INSERT INTO - Insert = 1, - /// Filter UPDATE - Update = 2, - /// Filter DELETE - Delete = 1 << 2, - /// Filter EXECUTE or EXEC - Execute = 1 << 3, - /// Filter ALTER - Alter = 1 << 4, - /// Filter CREATE - Create = 1 << 5, - /// Filter DROP - Drop = 1 << 6, - /// Filter TRUNCATE - Truncate = 1 << 7, - /// Filter SELECT - Select = 1 << 8, - /// Filter data manipulation commands consist of INSERT, INSERT INTO, UPDATE, and DELETE - DML = Insert | Update | Delete | Truncate, - /// Filter data definition commands consist of ALTER, CREATE, and DROP - DDL = Alter | Create | Drop, - /// Filter any SQL command types - All = DML | DDL | Execute | Select - } - public class RetryLogicTestHelper { private static readonly HashSet s_defaultTransientErrors @@ -75,152 +44,87 @@ private static readonly HashSet s_defaultTransientErrors 18456 // Using managed identity in Azure Sql Server throws 18456 for non-existent database instead of 4060. }; + public static readonly Regex FilterDmlStatements = new Regex( + @"\b(INSERT( +INTO)|UPDATE|DELETE|TRUNCATE)\b", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + internal static readonly string s_exceedErrMsgPattern = SystemDataResourceManager.Instance.SqlRetryLogic_RetryExceeded; internal static readonly string s_cancelErrMsgPattern = SystemDataResourceManager.Instance.SqlRetryLogic_RetryCanceled; - public static IEnumerable GetConnectionStrings() + public static TheoryData GetConnectionStringAndRetryProviders( + int numberOfRetries, + TimeSpan maxInterval, + TimeSpan? deltaTime = null, + IEnumerable transientErrorCodes = null, + Regex unauthorizedStatementRegex = null) { - var builder = new SqlConnectionStringBuilder(); - - foreach (var cnnString in DataTestUtility.GetConnectionStrings(withEnclave: false)) - { - builder.Clear(); - builder.ConnectionString = cnnString; - builder.ConnectTimeout = 5; - builder.Pooling = false; - yield return new object[] { builder.ConnectionString }; - - builder.Pooling = true; - yield return new object[] { builder.ConnectionString }; - } - } - - public static IEnumerable GetConnectionAndRetryStrategy(int numberOfRetries, - TimeSpan maxInterval, - FilterSqlStatements unauthorizedStatemets, - IEnumerable transientErrors, - int deltaTimeMillisecond = 10, - bool custom = true) - { - var option = new SqlRetryLogicOption() + var option = new SqlRetryLogicOption { NumberOfTries = numberOfRetries, - DeltaTime = TimeSpan.FromMilliseconds(deltaTimeMillisecond), + DeltaTime = deltaTime ?? TimeSpan.FromMilliseconds(10), MaxTimeInterval = maxInterval, - TransientErrors = transientErrors ?? (custom ? s_defaultTransientErrors : null), - AuthorizedSqlCondition = custom ? RetryPreConditon(unauthorizedStatemets) : null + TransientErrors = transientErrorCodes ?? s_defaultTransientErrors, + AuthorizedSqlCondition = RetryPreCondition(unauthorizedStatementRegex) }; - foreach (var item in GetRetryStrategies(option)) - foreach (var cnn in GetConnectionStrings()) - yield return new object[] { cnn[0], item[0] }; - } - - public static IEnumerable GetConnectionAndRetryStrategyInvalidCatalog(int numberOfRetries) - { - return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromSeconds(1), FilterSqlStatements.None, null, 250, true); - } + var result = new TheoryData(); + foreach (var connectionString in GetConnectionStringsTyped()) + { + foreach (var retryProvider in GetRetryStrategiesTyped(option)) + { + result.Add(connectionString, retryProvider); + } + } - public static IEnumerable GetConnectionAndRetryStrategyInvalidCommand(int numberOfRetries) - { - return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromMilliseconds(100), FilterSqlStatements.None, null); + return result; } - public static IEnumerable GetConnectionAndRetryStrategyFilterDMLStatements(int numberOfRetries) - { - return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromMilliseconds(100), FilterSqlStatements.DML, new int[] { 207, 102, 2812 }); - } + public static TheoryData GetNonRetriableCases() => + new TheoryData + { + { DataTestUtility.TCPConnectionString, null }, + { DataTestUtility.TCPConnectionString, SqlConfigurableRetryFactory.CreateNoneRetryProvider() } + }; - //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'. - public static IEnumerable GetConnectionAndRetryStrategyLongRunner(int numberOfRetries) + private static IEnumerable GetConnectionStringsTyped() { - return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromSeconds(120), FilterSqlStatements.None, null, 20 * 1000); - } + var builder = new SqlConnectionStringBuilder(); + foreach (var connectionString in DataTestUtility.GetConnectionStrings(withEnclave: false)) + { + builder.Clear(); + builder.ConnectionString = connectionString; + builder.ConnectTimeout = 5; + builder.Pooling = false; + yield return builder.ConnectionString; - public static IEnumerable GetConnectionAndRetryStrategyDropDB(int numberOfRetries) - { - List faults = s_defaultTransientErrors.ToList(); - faults.Add(3702); // Cannot drop database because it is currently in use. - return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromMilliseconds(2000), FilterSqlStatements.None, faults, 500); + builder.Pooling = true; + yield return builder.ConnectionString; + } } - public static IEnumerable GetConnectionAndRetryStrategyLockedTable(int numberOfRetries) + private static IEnumerable GetRetryStrategiesTyped(SqlRetryLogicOption option) { - return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromMilliseconds(100), FilterSqlStatements.None, null); + yield return SqlConfigurableRetryFactory.CreateExponentialRetryProvider(option); + yield return SqlConfigurableRetryFactory.CreateIncrementalRetryProvider(option); + yield return SqlConfigurableRetryFactory.CreateFixedRetryProvider(option); } - public static IEnumerable GetNoneRetriableCondition() + public static IEnumerable GetDefaultTransientErrorCodes(params int[] additionalCodes) { - yield return new object[] { DataTestUtility.TCPConnectionString, null }; - yield return new object[] { DataTestUtility.TCPConnectionString, SqlConfigurableRetryFactory.CreateNoneRetryProvider() }; - } + var transientErrorCodes = new HashSet(s_defaultTransientErrors); + foreach (int additionalCode in additionalCodes) + { + transientErrorCodes.Add(additionalCode); + } - private static IEnumerable GetRetryStrategies(SqlRetryLogicOption retryLogicOption) - { - yield return new object[] { SqlConfigurableRetryFactory.CreateExponentialRetryProvider(retryLogicOption) }; - yield return new object[] { SqlConfigurableRetryFactory.CreateIncrementalRetryProvider(retryLogicOption) }; - yield return new object[] { SqlConfigurableRetryFactory.CreateFixedRetryProvider(retryLogicOption) }; + return transientErrorCodes; } /// Generate a predicate function to skip unauthorized SQL commands. - private static Predicate RetryPreConditon(FilterSqlStatements unauthorizedSqlStatements) - { - var pattern = GetRegexPattern(unauthorizedSqlStatements); - return (commandText) => string.IsNullOrEmpty(pattern) - || !Regex.IsMatch(commandText, pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); - } - - /// Provide a regex pattern regarding to the SQL statement. - private static string GetRegexPattern(FilterSqlStatements sqlStatements) + private static Predicate RetryPreCondition(Regex unauthorizedStatementRegex) { - if (sqlStatements == FilterSqlStatements.None) - { - return string.Empty; - } - - var pattern = new StringBuilder(); - - if (sqlStatements.HasFlag(FilterSqlStatements.Insert)) - { - pattern.Append(@"INSERT( +INTO){0,1}|"); - } - if (sqlStatements.HasFlag(FilterSqlStatements.Update)) - { - pattern.Append(@"UPDATE|"); - } - if (sqlStatements.HasFlag(FilterSqlStatements.Delete)) - { - pattern.Append(@"DELETE|"); - } - if (sqlStatements.HasFlag(FilterSqlStatements.Execute)) - { - pattern.Append(@"EXEC(UTE){0,1}|"); - } - if (sqlStatements.HasFlag(FilterSqlStatements.Alter)) - { - pattern.Append(@"ALTER|"); - } - if (sqlStatements.HasFlag(FilterSqlStatements.Create)) - { - pattern.Append(@"CREATE|"); - } - if (sqlStatements.HasFlag(FilterSqlStatements.Drop)) - { - pattern.Append(@"DROP|"); - } - if (sqlStatements.HasFlag(FilterSqlStatements.Truncate)) - { - pattern.Append(@"TRUNCATE|"); - } - if (sqlStatements.HasFlag(FilterSqlStatements.Select)) - { - pattern.Append(@"SELECT|"); - } - if (pattern.Length > 0) - { - pattern.Remove(pattern.Length - 1, 1); - } - return string.Format(@"\b({0})\b", pattern.ToString()); + return commandText => unauthorizedStatementRegex is null || + !unauthorizedStatementRegex.IsMatch(commandText); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs index 21165e7624..bfdb426841 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlCommandReliabilityTest.cs @@ -45,8 +45,14 @@ protected virtual void Dispose(bool disposing) } #region Sync + + public static TheoryData RetryExecuteFail_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromMilliseconds(100)); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(RetryExecuteFail_Data), DisableDiscoveryEnumeration = true)] public void RetryExecuteFail(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries; @@ -90,8 +96,13 @@ public void RetryExecuteFail(string cnnString, SqlRetryLogicBaseProvider provide } } + public static TheoryData RetryExecuteCancel_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromMilliseconds(100)); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(RetryExecuteCancel_Data), DisableDiscoveryEnumeration = true)] public void RetryExecuteCancel(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries; @@ -135,9 +146,13 @@ public void RetryExecuteCancel(string cnnString, SqlRetryLogicBaseProvider provi } } - [ActiveIssue("14588")] + public static TheoryData RetryExecuteWithTransactionScope_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 5, + maxInterval: TimeSpan.FromMilliseconds(100)); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(RetryExecuteWithTransactionScope_Data), DisableDiscoveryEnumeration = true)] public void RetryExecuteWithTransScope(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries; @@ -171,10 +186,15 @@ public void RetryExecuteWithTransScope(string cnnString, SqlRetryLogicBaseProvid } } + public static TheoryData RetryExecuteWithTransaction_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 5, + maxInterval: TimeSpan.FromMilliseconds(100)); + // Synapse: 111214;An attempt to complete a transaction has failed. No corresponding transaction found. [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] - public void RetryExecuteWithTrans(string cnnString, SqlRetryLogicBaseProvider provider) + [MemberData(nameof(RetryExecuteWithTransaction_Data), DisableDiscoveryEnumeration = true)] + public void RetryExecuteWithTransaction(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries; int cancelAfterRetries = numberOfTries + 1; @@ -208,10 +228,17 @@ public void RetryExecuteWithTrans(string cnnString, SqlRetryLogicBaseProvider pr } } + public static TheoryData RetryExecuteUnauthorizedSqlStatementDml_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromMilliseconds(100), + transientErrorCodes: [102, 207, 2812], + unauthorizedStatementRegex: RetryLogicTestHelper.FilterDmlStatements); + // Synapse: Msg 103010, Level 16, State 1, Line 1 | Parse error at line: 1, column: 1: Incorrect syntax near 'command' [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyFilterDMLStatements), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] - public void RetryExecuteUnauthorizedSqlStatementDML(string cnnString, SqlRetryLogicBaseProvider provider) + [MemberData(nameof(RetryExecuteUnauthorizedSqlStatementDml_Data), DisableDiscoveryEnumeration = true)] + public void RetryExecuteUnauthorizedSqlStatementDml(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries; int cancelAfterRetries = numberOfTries + 1; @@ -221,7 +248,7 @@ public void RetryExecuteUnauthorizedSqlStatementDML(string cnnString, SqlRetryLo using (SqlConnection cnn = new SqlConnection(cnnString)) using (SqlCommand cmd = CreateCommand(cnn, provider, cancelAfterRetries)) { - #region unauthorized + // Unauthorized commands cmd.CommandText = "UPDATE bad command"; Assert.Throws(() => cmd.ExecuteNonQuery()); Assert.Equal(0, currentRetries); @@ -237,8 +264,8 @@ public void RetryExecuteUnauthorizedSqlStatementDML(string cnnString, SqlRetryLo cmd.CommandText = "TRUNCATE TABLE bad command"; Assert.Throws(() => cmd.ExecuteNonQuery()); Assert.Equal(0, currentRetries); - #endregion + // Authorized commands cmd.CommandText = "SELECT bad command"; Assert.Throws(() => cmd.ExecuteNonQuery()); Assert.Equal(numberOfTries, currentRetries + 1); @@ -261,10 +288,16 @@ public void RetryExecuteUnauthorizedSqlStatementDML(string cnnString, SqlRetryLo } } - [ActiveIssue("14325")] + public static TheoryData DropDatabaseWithActiveConnection_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 5, + maxInterval: TimeSpan.FromSeconds(2), + deltaTime: TimeSpan.FromMilliseconds(500), + transientErrorCodes: RetryLogicTestHelper.GetDefaultTransientErrorCodes(3702)); + // avoid creating a new database in Azure [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyDropDB), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(DropDatabaseWithActiveConnection_Data), DisableDiscoveryEnumeration = true)] public void DropDatabaseWithActiveConnection(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; @@ -323,11 +356,16 @@ public void DropDatabaseWithActiveConnection(string cnnString, SqlRetryLogicBase } } + public static TheoryData UpdateLockedTable_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 10, + maxInterval: TimeSpan.FromMilliseconds(100)); + // In Managed SNI by Named pipe connection, SqlCommand doesn't respect timeout. "ActiveIssue 12167" // Synapse: Does not support WAITFOR DELAY. [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotUsingManagedSNIOnWindows), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyLockedTable), parameters: new object[] { 10 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] - public void UpdateALockedTable(string cnnString, SqlRetryLogicBaseProvider provider) + [MemberData(nameof(UpdateLockedTable_Data), DisableDiscoveryEnumeration = true)] + public void UpdateLockedTable(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; string tableName = DataTestUtility.GetLongName("Region"); @@ -382,8 +420,11 @@ public void UpdateALockedTable(string cnnString, SqlRetryLogicBaseProvider provi } } + public static TheoryData NoneRetriableExecuteFail_Data => + RetryLogicTestHelper.GetNonRetriableCases(); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetNoneRetriableCondition), MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(NoneRetriableExecuteFail_Data), DisableDiscoveryEnumeration = true)] public void NoneRetriableExecuteFail(string cnnString, SqlRetryLogicBaseProvider provider) { string query = "SELECT bad command"; @@ -416,8 +457,14 @@ public void NoneRetriableExecuteFail(string cnnString, SqlRetryLogicBaseProvider #endregion #region Async + + public static TheoryData RetryExecuteAsyncFail_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromMilliseconds(100)); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(RetryExecuteAsyncFail_Data), DisableDiscoveryEnumeration = true)] public async Task RetryExecuteAsyncFail(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries; @@ -481,8 +528,13 @@ public async Task RetryExecuteAsyncFail(string cnnString, SqlRetryLogicBaseProvi } } + public static TheoryData RetryExecuteAsyncCancel_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromMilliseconds(100)); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(RetryExecuteAsyncCancel_Data), DisableDiscoveryEnumeration = true)] public async Task RetryExecuteAsyncCancel(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries; @@ -548,8 +600,14 @@ public async Task RetryExecuteAsyncCancel(string cnnString, SqlRetryLogicBasePro #endregion #region Concurrent + + public static TheoryData ConcurrentExecution_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromMilliseconds(100)); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(ConcurrentExecution_Data), DisableDiscoveryEnumeration = true)] public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider provider) { string query = "SELECT bad command"; @@ -565,8 +623,13 @@ public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider prov } } + public static TheoryData ConcurrentExecutionAsync_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromMilliseconds(100)); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCommand), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(ConcurrentExecutionAsync_Data), DisableDiscoveryEnumeration = true)] public async Task ConcurrentExecutionAsync(string cnnString, SqlRetryLogicBaseProvider provider) { string query = "SELECT bad command"; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs index e1590677dd..c52ea76ec5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs @@ -48,7 +48,8 @@ public void LoadValidInternalTypes(string method1, string method2) s_commandCRLTest.RetryExecuteFail(TcpCnnString, cmdProvider); if (DataTestUtility.IsNotAzureSynapse()) { - s_commandCRLTest.RetryExecuteUnauthorizedSqlStatementDML(TcpCnnString, cmdProvider); + // @TODO: Why are we calling a test from another test? + s_commandCRLTest.RetryExecuteUnauthorizedSqlStatementDml(TcpCnnString, cmdProvider); } } #endregion diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs index e5ed05e09f..952457744e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConnectionReliabilityTest.cs @@ -16,9 +16,16 @@ public class SqlConnectionReliabilityTest private readonly string _cancelErrMsgPattern = RetryLogicTestHelper.s_cancelErrMsgPattern; #region Sync + + public static TheoryData ConnectionRetryOpenInvalidCatalogFailed_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromSeconds(1), + deltaTime: TimeSpan.FromMilliseconds(250)); + // Test relies on error 4060 for automatic retry, which is not reliable when using Azure or AAD auth [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(ConnectionRetryOpenInvalidCatalogFailed_Data), DisableDiscoveryEnumeration = true)] public void ConnectionRetryOpenInvalidCatalogFailed(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries; @@ -34,9 +41,15 @@ public void ConnectionRetryOpenInvalidCatalogFailed(string cnnString, SqlRetryLo } } + public static TheoryData ConnectionCancelRetryOpenInvalidCatalog_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromSeconds(1), + deltaTime: TimeSpan.FromMilliseconds(250)); + // Test relies on error 4060 for automatic retry, which is not reliable when using Azure or AAD auth [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(ConnectionCancelRetryOpenInvalidCatalog_Data), DisableDiscoveryEnumeration = true)] public void ConnectionCancelRetryOpenInvalidCatalog(string cnnString, SqlRetryLogicBaseProvider provider) { int cancelAfterRetries = provider.RetryLogic.NumberOfTries - 1; @@ -51,10 +64,15 @@ public void ConnectionCancelRetryOpenInvalidCatalog(string cnnString, SqlRetryLo } } - [ActiveIssue("14590", TestPlatforms.Windows)] + public static TheoryData CreateDatabaseWhileTryingToConnect_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 10, + maxInterval: TimeSpan.FromSeconds(10), + deltaTime: TimeSpan.FromSeconds(1)); + // avoid creating a new database in Azure [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyLongRunner), parameters: new object[] { 10 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(CreateDatabaseWhileTryingToConnect_Data), DisableDiscoveryEnumeration = true)] public void CreateDatabaseWhileTryingToConnect(string cnnString, SqlRetryLogicBaseProvider provider) { int currentRetries = 0; @@ -100,9 +118,14 @@ public void CreateDatabaseWhileTryingToConnect(string cnnString, SqlRetryLogicBa Assert.True(currentRetries > 0); } - [ActiveIssue("25147")] + public static TheoryData ConcurrentExecution_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromSeconds(1), + deltaTime: TimeSpan.FromMilliseconds(250)); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(ConcurrentExecution_Data), DisableDiscoveryEnumeration = true)] public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries; @@ -133,8 +156,11 @@ public void ConcurrentExecution(string cnnString, SqlRetryLogicBaseProvider prov Assert.Equal(numberOfTries * concurrentExecution, retriesCount + concurrentExecution); } + public static TheoryData DefaultOpenWithoutRetry_Data => + RetryLogicTestHelper.GetNonRetriableCases(); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetNoneRetriableCondition), MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(DefaultOpenWithoutRetry_Data), DisableDiscoveryEnumeration = true)] public void DefaultOpenWithoutRetry(string connectionString, SqlRetryLogicBaseProvider cnnProvider) { var cnnString = new SqlConnectionStringBuilder(connectionString) @@ -157,9 +183,16 @@ public void DefaultOpenWithoutRetry(string connectionString, SqlRetryLogicBasePr #endregion #region Async + + public static TheoryData ConnectionRetryOpenAsyncInvalidCatalogFailed_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 5, + maxInterval: TimeSpan.FromSeconds(1), + deltaTime: TimeSpan.FromMilliseconds(250)); + // Test relies on error 4060 for automatic retry, which is not reliable when using Azure or AAD auth [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 5 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(ConnectionRetryOpenAsyncInvalidCatalogFailed_Data), DisableDiscoveryEnumeration = true)] public async Task ConnectionRetryOpenAsyncInvalidCatalogFailed(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries; @@ -175,9 +208,15 @@ public async Task ConnectionRetryOpenAsyncInvalidCatalogFailed(string cnnString, } } + public static TheoryData ConnectionCancelRetryOpenAsyncInvalidCatalog_Data => + RetryLogicTestHelper.GetConnectionStringAndRetryProviders( + numberOfRetries: 2, + maxInterval: TimeSpan.FromSeconds(1), + deltaTime: TimeSpan.FromMilliseconds(250)); + // Test relies on error 4060 for automatic retry, which is not returned when using AAD auth [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.TcpConnectionStringDoesNotUseAadAuth))] - [MemberData(nameof(RetryLogicTestHelper.GetConnectionAndRetryStrategyInvalidCatalog), parameters: new object[] { 2 }, MemberType = typeof(RetryLogicTestHelper), DisableDiscoveryEnumeration = true)] + [MemberData(nameof(ConnectionCancelRetryOpenAsyncInvalidCatalog_Data), DisableDiscoveryEnumeration = true)] public async Task ConnectionCancelRetryOpenAsyncInvalidCatalog(string cnnString, SqlRetryLogicBaseProvider provider) { int numberOfTries = provider.RetryLogic.NumberOfTries;