-
Notifications
You must be signed in to change notification settings - Fork 298
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Port connection pool tests from devdiv (#1155)
- v6.0.1
- v6.0.0-preview3
- v6.0.0-preview2
- v6.0.0-preview1
- v5.2.2
- v5.2.1
- v5.2.0-preview5
- v5.2.0-preview4
- v5.2.0-preview3
- v5.2.0-preview2
- v5.2.0-preview1
- v5.1.6
- v5.1.5
- v5.1.4
- v5.1.3
- v5.1.2
- v5.1.1
- v5.1.0
- v5.1.0-preview2
- v5.1.0-preview1
- v5.0.2
- v5.0.1
- v5.0.0
- v5.0.0-preview3
- v5.0.0-preview2
- v5.0.0-preview1
- v4.1.1
- v4.1.0
- v4.0.6
- v4.0.5
- v4.0.4
- v4.0.3
- v4.0.2
- v4.0.1
- v4.0.0
- v4.0.0-preview3
- v4.0.0-preview2
- v3.1.0
- v3.0.1
- V5.2.0
- MSS_v1.0.0
- AKV_5.1.0
Johnny Pham
authored
Sep 20, 2021
1 parent
a93f86a
commit 96a8b05
Showing
9 changed files
with
413 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
193 changes: 193 additions & 0 deletions
193
...osoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.Debug.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
using System; | ||
using System.Runtime.ExceptionServices; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Xunit; | ||
using static Microsoft.Data.SqlClient.ManualTesting.Tests.ConnectionPoolTest; | ||
|
||
namespace Microsoft.Data.SqlClient.ManualTesting.Tests | ||
{ | ||
public static class ConnectionPoolTestDebug | ||
{ | ||
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsUsingManagedSNI))] | ||
[ClassData(typeof(ConnectionPoolConnectionStringProvider))] | ||
public static void ReplacementConnectionUsesSemaphoreTest(string connectionString) | ||
{ | ||
string newConnectionString = (new SqlConnectionStringBuilder(connectionString) { MaxPoolSize = 2, ConnectTimeout = 5 }).ConnectionString; | ||
SqlConnection.ClearAllPools(); | ||
|
||
using SqlConnection liveConnection = new(newConnectionString); | ||
using SqlConnection deadConnection = new(newConnectionString); | ||
liveConnection.Open(); | ||
deadConnection.Open(); | ||
InternalConnectionWrapper deadConnectionInternal = new(deadConnection); | ||
InternalConnectionWrapper liveConnectionInternal = new(liveConnection); | ||
deadConnectionInternal.KillConnection(); | ||
deadConnection.Close(); | ||
liveConnection.Close(); | ||
|
||
Task<InternalConnectionWrapper>[] tasks = new Task<InternalConnectionWrapper>[3]; | ||
Barrier syncBarrier = new(tasks.Length); | ||
Func<InternalConnectionWrapper> taskFunction = (() => ReplacementConnectionUsesSemaphoreTask(newConnectionString, syncBarrier)); | ||
for (int i = 0; i < tasks.Length; i++) | ||
{ | ||
tasks[i] = Task.Factory.StartNew(taskFunction); | ||
} | ||
|
||
bool taskWithLiveConnection = false; | ||
bool taskWithNewConnection = false; | ||
bool taskWithCorrectException = false; | ||
|
||
Task waitAllTask = Task.Factory.ContinueWhenAll(tasks, (completedTasks) => | ||
{ | ||
foreach (var item in completedTasks) | ||
{ | ||
if (item.Status == TaskStatus.Faulted) | ||
{ | ||
// One task should have a timeout exception | ||
if ((!taskWithCorrectException) && (item.Exception.InnerException is InvalidOperationException) && (item.Exception.InnerException.Message.StartsWith(SystemDataResourceManager.Instance.ADP_PooledOpenTimeout))) | ||
taskWithCorrectException = true; | ||
else if (!taskWithCorrectException) | ||
{ | ||
// Rethrow the unknown exception | ||
ExceptionDispatchInfo exceptionInfo = ExceptionDispatchInfo.Capture(item.Exception); | ||
exceptionInfo.Throw(); | ||
} | ||
} | ||
else if (item.Status == TaskStatus.RanToCompletion) | ||
{ | ||
// One task should get the live connection | ||
if (item.Result.Equals(liveConnectionInternal)) | ||
{ | ||
if (!taskWithLiveConnection) | ||
taskWithLiveConnection = true; | ||
} | ||
else if (!item.Result.Equals(deadConnectionInternal) && !taskWithNewConnection) | ||
taskWithNewConnection = true; | ||
} | ||
else | ||
Console.WriteLine("ERROR: Task in unknown state: {0}", item.Status); | ||
} | ||
}); | ||
|
||
waitAllTask.Wait(); | ||
Assert.True(taskWithLiveConnection && taskWithNewConnection && taskWithCorrectException, | ||
$"Tasks didn't finish as expected.\n" + | ||
$"Task with live connection: {taskWithLiveConnection}\n" + | ||
$"Task with new connection: {taskWithNewConnection}\n" + | ||
$"Task with correct exception: {taskWithCorrectException}\n"); | ||
} | ||
|
||
/// <summary> | ||
/// Tests if killing the connection using the InternalConnectionWrapper is working | ||
/// </summary> | ||
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsUsingManagedSNI))] | ||
[ClassData(typeof(ConnectionPoolConnectionStringProvider))] | ||
public static void KillConnectionTest(string connectionString) | ||
{ | ||
InternalConnectionWrapper wrapper = null; | ||
|
||
using (SqlConnection connection = new(connectionString)) | ||
{ | ||
connection.Open(); | ||
wrapper = new InternalConnectionWrapper(connection); | ||
|
||
using SqlCommand command = new("SELECT 5;", connection); | ||
|
||
DataTestUtility.AssertEqualsWithDescription(5, command.ExecuteScalar(), "Incorrect scalar result."); | ||
|
||
wrapper.KillConnection(); | ||
} | ||
|
||
using (SqlConnection connection2 = new(connectionString)) | ||
{ | ||
connection2.Open(); | ||
Assert.False(wrapper.IsInternalConnectionOf(connection2), "New connection has internal connection that was just killed"); | ||
using SqlCommand command = new("SELECT 5;", connection2); | ||
|
||
DataTestUtility.AssertEqualsWithDescription(5, command.ExecuteScalar(), "Incorrect scalar result."); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Tests that cleanup removes connections that are unused for two cleanups | ||
/// </summary> | ||
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsUsingManagedSNI))] | ||
[ClassData(typeof(ConnectionPoolConnectionStringProvider))] | ||
public static void CleanupTest(string connectionString) | ||
{ | ||
SqlConnection.ClearAllPools(); | ||
|
||
using SqlConnection conn1 = new(connectionString); | ||
using SqlConnection conn2 = new(connectionString); | ||
conn1.Open(); | ||
conn2.Open(); | ||
ConnectionPoolWrapper connectionPool = new(conn1); | ||
Assert.Equal(2, connectionPool.ConnectionCount); | ||
|
||
connectionPool.Cleanup(); | ||
Assert.Equal(2, connectionPool.ConnectionCount); | ||
|
||
conn1.Close(); | ||
connectionPool.Cleanup(); | ||
Assert.Equal(2, connectionPool.ConnectionCount); | ||
|
||
conn2.Close(); | ||
connectionPool.Cleanup(); | ||
Assert.Equal(1, connectionPool.ConnectionCount); | ||
|
||
connectionPool.Cleanup(); | ||
Assert.Equal(0, connectionPool.ConnectionCount); | ||
|
||
using SqlConnection conn3 = new(connectionString); | ||
conn3.Open(); | ||
InternalConnectionWrapper internalConnection3 = new(conn3); | ||
|
||
conn3.Close(); | ||
internalConnection3.KillConnection(); | ||
Assert.Equal(1, connectionPool.ConnectionCount); | ||
Assert.False(internalConnection3.IsConnectionAlive(), "Connection should not be alive"); | ||
|
||
connectionPool.Cleanup(); | ||
Assert.Equal(1, connectionPool.ConnectionCount); | ||
} | ||
|
||
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsUsingManagedSNI))] | ||
[ClassData(typeof(ConnectionPoolConnectionStringProvider))] | ||
public static void ReplacementConnectionObeys0TimeoutTest(string connectionString) | ||
{ | ||
string newConnectionString = (new SqlConnectionStringBuilder(connectionString) { ConnectTimeout = 0 }).ConnectionString; | ||
SqlConnection.ClearAllPools(); | ||
|
||
// Kick off proxy | ||
using (ProxyServer proxy = ProxyServer.CreateAndStartProxy(newConnectionString, out newConnectionString)) | ||
{ | ||
// Create one dead connection | ||
using SqlConnection deadConnection = new(newConnectionString); | ||
deadConnection.Open(); | ||
InternalConnectionWrapper deadConnectionInternal = new(deadConnection); | ||
deadConnectionInternal.KillConnection(); | ||
|
||
// Block one live connection | ||
proxy.PauseCopying(); | ||
Task<SqlConnection> blockedConnectionTask = Task.Run(() => ReplacementConnectionObeys0TimeoutTask(newConnectionString)); | ||
Thread.Sleep(100); | ||
Assert.Equal(TaskStatus.Running, blockedConnectionTask.Status); | ||
|
||
// Close and re-open the dead connection | ||
deadConnection.Close(); | ||
Task<SqlConnection> newConnectionTask = Task.Run(() => ReplacementConnectionObeys0TimeoutTask(newConnectionString)); | ||
Thread.Sleep(100); | ||
Assert.Equal(TaskStatus.Running, blockedConnectionTask.Status); | ||
Assert.Equal(TaskStatus.Running, newConnectionTask.Status); | ||
|
||
// restart the proxy | ||
proxy.ResumeCopying(); | ||
|
||
Task.WaitAll(blockedConnectionTask, newConnectionTask); | ||
blockedConnectionTask.Result.Close(); | ||
newConnectionTask.Result.Close(); | ||
} | ||
} | ||
} | ||
} |
194 changes: 67 additions & 127 deletions
194
src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs
Large diffs are not rendered by default.
Oops, something went wrong.
99 changes: 99 additions & 0 deletions
99
src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/TransactionPoolTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
using System.Transactions; | ||
using Xunit; | ||
|
||
namespace Microsoft.Data.SqlClient.ManualTesting.Tests | ||
{ | ||
public static class TransactionPoolTest | ||
{ | ||
/// <summary> | ||
/// Tests if connections in a distributed transaction are put into a transaction pool. Also checks that clearallpools | ||
/// does not clear transaction connections and that the transaction root is put into "stasis" when closed | ||
/// </summary> | ||
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] | ||
[ClassData(typeof(ConnectionPoolConnectionStringProvider))] | ||
public static void BasicTransactionPoolTest(string connectionString) | ||
{ | ||
SqlConnection.ClearAllPools(); | ||
ConnectionPoolWrapper connectionPool = null; | ||
|
||
using (TransactionScope transScope = new()) | ||
{ | ||
using SqlConnection connection1 = new(connectionString); | ||
using SqlConnection connection2 = new(connectionString); | ||
connection1.Open(); | ||
connection2.Open(); | ||
connectionPool = new ConnectionPoolWrapper(connection1); | ||
|
||
InternalConnectionWrapper internalConnection1 = new(connection1); | ||
InternalConnectionWrapper internalConnection2 = new(connection2); | ||
|
||
Assert.True(internalConnection1.IsEnlistedInTransaction, "First connection not in transaction"); | ||
Assert.True(internalConnection1.IsTransactionRoot, "First connection not transaction root"); | ||
Assert.True(internalConnection2.IsEnlistedInTransaction, "Second connection not in transaction"); | ||
Assert.False(internalConnection2.IsTransactionRoot, "Second connection is transaction root"); | ||
|
||
// Attempt to re-use root connection | ||
connection1.Close(); | ||
using SqlConnection connection3 = new(connectionString); | ||
connection3.Open(); | ||
|
||
Assert.True(connectionPool.ContainsConnection(connection3), "New connection in wrong pool"); | ||
Assert.True(internalConnection1.IsInternalConnectionOf(connection3), "Root connection was not re-used"); | ||
|
||
// Attempt to re-use non-root connection | ||
connection2.Close(); | ||
using SqlConnection connection4 = new(connectionString); | ||
connection4.Open(); | ||
Assert.True(internalConnection2.IsInternalConnectionOf(connection4), "Connection did not re-use expected internal connection"); | ||
Assert.True(connectionPool.ContainsConnection(connection4), "New connection is in the wrong pool"); | ||
connection4.Close(); | ||
|
||
// Use a different connection string | ||
using SqlConnection connection5 = new(connectionString + ";App=SqlConnectionPoolUnitTest;"); | ||
connection5.Open(); | ||
Assert.False(internalConnection2.IsInternalConnectionOf(connection5), "Connection with different connection string re-used internal connection"); | ||
Assert.False(connectionPool.ContainsConnection(connection5), "Connection with different connection string is in same pool"); | ||
connection5.Close(); | ||
|
||
transScope.Complete(); | ||
} | ||
|
||
Assert.Equal(2, connectionPool.ConnectionCount); | ||
} | ||
|
||
/// <summary> | ||
/// Checks that connections in the transaction pool are not cleaned out, and the root transaction is put into "stasis" when it ages | ||
/// </summary> | ||
/// <param name="connectionString"></param> | ||
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] | ||
[ClassData(typeof(ConnectionPoolConnectionStringProvider))] | ||
public static void TransactionCleanupTest(string connectionString) | ||
{ | ||
SqlConnection.ClearAllPools(); | ||
ConnectionPoolWrapper connectionPool = null; | ||
|
||
using (TransactionScope transScope = new()) | ||
{ | ||
using SqlConnection connection1 = new(connectionString); | ||
using SqlConnection connection2 = new(connectionString); | ||
connection1.Open(); | ||
connection2.Open(); | ||
InternalConnectionWrapper internalConnection1 = new(connection1); | ||
connectionPool = new ConnectionPoolWrapper(connection1); | ||
|
||
connectionPool.Cleanup(); | ||
Assert.Equal(2, connectionPool.ConnectionCount); | ||
|
||
connection1.Close(); | ||
connection2.Close(); | ||
connectionPool.Cleanup(); | ||
Assert.Equal(2, connectionPool.ConnectionCount); | ||
|
||
connectionPool.Cleanup(); | ||
Assert.Equal(2, connectionPool.ConnectionCount); | ||
|
||
transScope.Complete(); | ||
} | ||
} | ||
} | ||
} |