Fixes deadlock issues and concurrent index drop issues

This commit is contained in:
Shannon
2020-04-03 13:54:28 +11:00
parent 60abdd60b5
commit 859546f5a4
3 changed files with 58 additions and 43 deletions

View File

@@ -53,11 +53,11 @@ namespace Umbraco.Tests.Integration.Extensions
{
case UmbracoTestOptions.Database.NewSchemaPerTest:
// Add teardown callback
integrationTest.OnTestTearDown(() => db.Detach());
// New DB + Schema
db.AttachSchema();
var newSchemaDbId = db.AttachSchema();
// Add teardown callback
integrationTest.OnTestTearDown(() => db.Detach(newSchemaDbId));
// We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings
var dbFactory = app.ApplicationServices.GetRequiredService<IUmbracoDatabaseFactory>();
@@ -89,22 +89,27 @@ namespace Umbraco.Tests.Integration.Extensions
break;
case UmbracoTestOptions.Database.NewEmptyPerTest:
// Add teardown callback
integrationTest.OnTestTearDown(() => db.Detach());
var newEmptyDbId = db.AttachEmpty();
// Add teardown callback
integrationTest.OnTestTearDown(() => db.Detach(newEmptyDbId));
db.AttachEmpty();
break;
case UmbracoTestOptions.Database.NewSchemaPerFixture:
// Add teardown callback
integrationTest.OnFixtureTearDown(() => db.Detach());
throw new NotImplementedException();
//// Add teardown callback
//integrationTest.OnFixtureTearDown(() => db.Detach());
break;
case UmbracoTestOptions.Database.NewEmptyPerFixture:
// Add teardown callback
integrationTest.OnFixtureTearDown(() => db.Detach());
throw new NotImplementedException();
//// Add teardown callback
//integrationTest.OnFixtureTearDown(() => db.Detach());
break;
default:

View File

@@ -78,27 +78,29 @@ namespace Umbraco.Tests.Integration.Testing
_schemaPool = new DatabasePool(_localDb, _instance, DatabaseName + "-Schema", tempName, _filesPath, schemaSize, schemaParallel, delete: true, prepare: RebuildSchema);
}
public void AttachEmpty()
public int AttachEmpty()
{
if (_emptyPool == null)
Create();
_currentCstr = _emptyPool.AttachDatabase();
_currentCstr = _emptyPool.AttachDatabase(out var id);
_currentPool = _emptyPool;
return id;
}
public void AttachSchema()
public int AttachSchema()
{
if (_schemaPool == null)
Create();
_currentCstr = _schemaPool.AttachDatabase();
_currentCstr = _schemaPool.AttachDatabase(out var id);
_currentPool = _schemaPool;
return id;
}
public void Detach()
public void Detach(int id)
{
_currentPool.DetachDatabase();
_currentPool.DetachDatabase(id);
}
private void RebuildSchema(DbConnection conn, IDbCommand cmd)
@@ -188,7 +190,25 @@ namespace Umbraco.Tests.Integration.Testing
from sys.tables;
exec sp_executesql @stmt;
";
cmd.ExecuteNonQuery();
// rudimentary retry policy since a db can still be in use when we try to drop
for (var i = 0; i < 5; i++)
{
try
{
cmd.ExecuteNonQuery();
return;
}
catch (SqlException ex)
{
// This can occur when there's a transaction deadlock which means (i think) that the database is still in use and hasn't been closed properly yet
// so we need to just wait a little bit
Thread.Sleep(1000);
if (i == 4)
throw;
}
}
}
public static void KillLocalDb()
@@ -266,22 +286,19 @@ namespace Umbraco.Tests.Integration.Testing
}
}
public string AttachDatabase()
public string AttachDatabase(out int id)
{
try
{
_current = _readyQueue.Take();
}
catch (InvalidOperationException)
{
_current = 0;
return null;
}
_current = _readyQueue.Take();
id = _current;
return ConnectionString(_current);
}
public void DetachDatabase()
public void DetachDatabase(int id)
{
if (id != _current)
throw new InvalidOperationException("Cannot detatch the non-current db");
_prepareQueue.Add(_current);
}

View File

@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NPoco.Expressions;
using NUnit.Framework;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
@@ -70,37 +71,29 @@ namespace Umbraco.Tests.Integration.Testing
private static readonly object _dbLocker = new object();
private static LocalDbTestDatabase _dbInstance;
private readonly List<Action> _testTeardown = new List<Action>();
private readonly List<Action> _fixtureTeardown = new List<Action>();
private Action _testTeardown = null;
private Action _fixtureTeardown = null;
public void OnTestTearDown(Action tearDown)
{
_testTeardown.Add(tearDown);
_testTeardown = tearDown;
}
public void OnFixtureTearDown(Action tearDown)
{
_fixtureTeardown.Add(tearDown);
_fixtureTeardown = tearDown;
}
[OneTimeTearDown]
public void FixtureTearDown()
{
// call all registered callbacks
foreach (var action in _fixtureTeardown)
{
action();
}
_fixtureTeardown?.Invoke();
}
[TearDown]
public void TearDown()
{
// call all registered callbacks
foreach (var action in _testTeardown)
{
action();
}
_testTeardown?.Invoke();
}
[SetUp]