Add managed connection pooling and execution locking
This commit is contained in:
153
DynamORM.Tests/Helpers/ConnectionPoolingAndLockingTests.cs
Normal file
153
DynamORM.Tests/Helpers/ConnectionPoolingAndLockingTests.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace DynamORM.Tests.Helpers
|
||||
{
|
||||
[TestFixture]
|
||||
public class ConnectionPoolingAndLockingTests
|
||||
{
|
||||
private FakeProviderState _state;
|
||||
private DynamicDatabase _db;
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
if (_db != null)
|
||||
_db.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConnectionPoolingReusesIdleConnection()
|
||||
{
|
||||
CreateDatabase(DynamicDatabaseOptions.ConnectionPooling);
|
||||
|
||||
using (_db.Open()) { }
|
||||
using (_db.Open()) { }
|
||||
|
||||
Assert.AreEqual(1, _state.CreatedConnections);
|
||||
Assert.AreEqual(1, _state.OpenCalls);
|
||||
Assert.AreEqual(0, _state.CloseCalls);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConnectionPoolingWaitsForReleasedConnectionWhenAtMaximum()
|
||||
{
|
||||
CreateDatabase(DynamicDatabaseOptions.ConnectionPooling);
|
||||
_db.ConnectionPoolingKeepOpenCount = 1;
|
||||
_db.ConnectionPoolingMaximumOpenCount = 1;
|
||||
|
||||
var first = _db.Open();
|
||||
var started = new ManualResetEventSlim(false);
|
||||
var completed = new ManualResetEventSlim(false);
|
||||
|
||||
var task = Task.Run(() =>
|
||||
{
|
||||
started.Set();
|
||||
using (_db.Open()) { }
|
||||
completed.Set();
|
||||
});
|
||||
|
||||
Assert.IsTrue(started.Wait(TimeSpan.FromSeconds(2)));
|
||||
Assert.IsFalse(completed.Wait(TimeSpan.FromMilliseconds(200)));
|
||||
|
||||
first.Dispose();
|
||||
|
||||
Assert.IsTrue(completed.Wait(TimeSpan.FromSeconds(2)));
|
||||
task.Wait(TimeSpan.FromSeconds(2));
|
||||
Assert.AreEqual(1, _state.CreatedConnections);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConnectionPoolingClosesExpiredIdleConnections()
|
||||
{
|
||||
CreateDatabase(DynamicDatabaseOptions.ConnectionPooling);
|
||||
_db.ConnectionPoolingConnectionLifetime = TimeSpan.Zero;
|
||||
|
||||
using (_db.Open()) { }
|
||||
using (_db.Open()) { }
|
||||
|
||||
Assert.AreEqual(2, _state.CreatedConnections);
|
||||
Assert.GreaterOrEqual(_state.CloseCalls, 1);
|
||||
Assert.GreaterOrEqual(_state.DisposeCalls, 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDirectTransactionUsesSameThreadConnectionAndSeparateThreadGetsDifferentOne()
|
||||
{
|
||||
CreateDatabase(DynamicDatabaseOptions.ConnectionPooling);
|
||||
|
||||
using (var trans = _db.BeginTransaction())
|
||||
{
|
||||
IDbConnection threadLocalA = _db.Open();
|
||||
IDbConnection threadLocalB = _db.Open();
|
||||
|
||||
try
|
||||
{
|
||||
Assert.AreSame(((DynamicConnection)threadLocalA).Connection, ((DynamicConnection)threadLocalB).Connection);
|
||||
|
||||
IDbConnection otherThreadConnection = null;
|
||||
var task = Task.Run(() =>
|
||||
{
|
||||
using (var other = _db.Open())
|
||||
otherThreadConnection = ((DynamicConnection)other).Connection;
|
||||
});
|
||||
|
||||
Assert.IsTrue(task.Wait(TimeSpan.FromSeconds(2)));
|
||||
Assert.AreNotSame(((DynamicConnection)threadLocalA).Connection, otherThreadConnection);
|
||||
}
|
||||
finally
|
||||
{
|
||||
threadLocalA.Dispose();
|
||||
threadLocalB.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(DynamicDatabaseOptions.SingleConnection)]
|
||||
[TestCase(DynamicDatabaseOptions.SingleTransaction)]
|
||||
public void TestSingleModesSerializeCommandExecution(DynamicDatabaseOptions option)
|
||||
{
|
||||
CreateDatabase(option);
|
||||
_state.BlockFirstExecution = true;
|
||||
_state.AllowExecution.Reset();
|
||||
|
||||
Task task1 = Task.Run(() => ExecuteFakeCommand());
|
||||
Assert.IsTrue(_state.FirstExecutionEntered.Wait(TimeSpan.FromSeconds(2)));
|
||||
|
||||
Task task2 = Task.Run(() => ExecuteFakeCommand());
|
||||
Thread.Sleep(200);
|
||||
|
||||
Assert.AreEqual(1, _state.MaxConcurrentExecutions);
|
||||
|
||||
_state.AllowExecution.Set();
|
||||
|
||||
Assert.IsTrue(Task.WaitAll(new[] { task1, task2 }, TimeSpan.FromSeconds(5)));
|
||||
Assert.AreEqual(1, _state.MaxConcurrentExecutions);
|
||||
}
|
||||
|
||||
private void ExecuteFakeCommand()
|
||||
{
|
||||
using (var connection = _db.Open())
|
||||
using (var command = connection.CreateCommand())
|
||||
{
|
||||
command.SetCommand("SELECT 1;");
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateDatabase(DynamicDatabaseOptions options)
|
||||
{
|
||||
_state = new FakeProviderState();
|
||||
_db = new DynamicDatabase(new FakeProviderFactory(_state), "fake", options);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user