forked from DbKeeperNet/DbKeeperNet
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request DbKeeperNet#49 from voloda/Locking
Perform db upgrade mutually exlusively when executed on multiple nodes at the same time
- Loading branch information
Showing
96 changed files
with
1,330 additions
and
136 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
using Microsoft.Extensions.Logging.Abstractions; | ||
using Moq; | ||
using NUnit.Framework; | ||
|
||
namespace DbKeeperNet.Engine.Tests.Full | ||
{ | ||
/// <summary> | ||
/// Test class for <see cref="DatabaseLockService"/> | ||
/// </summary> | ||
[TestFixture] | ||
public class DatabaseLockServiceTest | ||
{ | ||
private Mock<IDatabaseLock> _databaseLock; | ||
private const int LockId = 1; | ||
|
||
[SetUp] | ||
public void Setup() | ||
{ | ||
_databaseLock = new Mock<IDatabaseLock>(MockBehavior.Strict); | ||
} | ||
|
||
[Test] | ||
public void IfDatabaseLockIsNotSupportedLockShouldDoNothing() | ||
{ | ||
_databaseLock.Setup(l => l.IsSupported).Returns(false); | ||
|
||
var service = CreateDatabaseLockService(); | ||
|
||
var locker = service.AcquireLock(LockId); | ||
locker.Dispose(); | ||
} | ||
|
||
[Test] | ||
public void IfDatabaseLockIsSupportedAndAcquiredDisposeShouldRelease() | ||
{ | ||
_databaseLock.Setup(l => l.IsSupported).Returns(true); | ||
_databaseLock.Setup(l => l.Acquire(LockId, It.IsAny<string>(), It.IsAny<int>())).Returns(true); | ||
_databaseLock.Setup(l => l.Release(LockId)); | ||
|
||
var service = CreateDatabaseLockService(); | ||
|
||
var locker = service.AcquireLock(LockId); | ||
|
||
_databaseLock.Verify(l => l.Acquire(LockId, It.IsAny<string>(), It.IsAny<int>()), Times.Once); | ||
_databaseLock.Verify(l => l.Release(LockId), Times.Never); | ||
|
||
locker.Dispose(); | ||
|
||
_databaseLock.Verify(l => l.Release(LockId), Times.Once); | ||
} | ||
|
||
[Test] | ||
public void AcquireShouldTryUntilItSucceeds() | ||
{ | ||
var sequence = new MockSequence(); | ||
_databaseLock.Setup(l => l.IsSupported).Returns(true); | ||
_databaseLock.InSequence(sequence).Setup(l => l.Acquire(LockId, It.IsAny<string>(), It.IsAny<int>())).Returns(false); | ||
_databaseLock.InSequence(sequence).Setup(l => l.Acquire(LockId, It.IsAny<string>(), It.IsAny<int>())).Returns(true); | ||
_databaseLock.Setup(l => l.Release(LockId)); | ||
|
||
var service = CreateDatabaseLockService(); | ||
|
||
var locker = service.AcquireLock(LockId); | ||
|
||
_databaseLock.Verify(l => l.Acquire(LockId, It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2)); | ||
_databaseLock.Verify(l => l.Release(LockId), Times.Never); | ||
|
||
locker.Dispose(); | ||
|
||
_databaseLock.Verify(l => l.Release(LockId), Times.Once); | ||
} | ||
|
||
private DatabaseLockService CreateDatabaseLockService() | ||
{ | ||
var service = new DatabaseLockService(NullLogger<DatabaseLockService>.Instance, _databaseLock.Object); | ||
|
||
return service; | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using NUnit.Framework; | ||
|
||
namespace DbKeeperNet.Engine.Tests.Full | ||
{ | ||
[TestFixture] | ||
public class NullDatabaseLockTest | ||
{ | ||
[Test] | ||
public void IsSupportedShouldReturnFalse() | ||
{ | ||
var databaseLock = CreateNullDatabaseLock(); | ||
|
||
Assert.That(databaseLock.IsSupported, Is.False); | ||
} | ||
|
||
private static NullDatabaseLock CreateNullDatabaseLock() | ||
{ | ||
var databaseLock = new NullDatabaseLock(); | ||
return databaseLock; | ||
} | ||
|
||
[Test] | ||
public void ReleaseShouldDoNothing() | ||
{ | ||
var databaseLock = CreateNullDatabaseLock(); | ||
|
||
databaseLock.Release(0); | ||
} | ||
|
||
[Test] | ||
public void AcquireShouldReturnTrue() | ||
{ | ||
var databaseLock = CreateNullDatabaseLock(); | ||
|
||
Assert.That(databaseLock.Acquire(0, null, 0), Is.True); | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using Microsoft.Extensions.DependencyInjection; | ||
using NUnit.Framework; | ||
|
||
namespace DbKeeperNet.Engine.Tests | ||
{ | ||
/// <summary> | ||
/// Base test class to excercise <see cref="IDatabaseLock"/> implementations | ||
/// </summary> | ||
public abstract class DatabaseLockTests : TestBase | ||
{ | ||
protected const int TestLockId = 4262; | ||
|
||
[SetUp] | ||
public override void Setup() | ||
{ | ||
base.Setup(); | ||
|
||
var upgrader = DefaultScope.ServiceProvider.GetService<IDatabaseUpdater>(); | ||
upgrader.ExecuteUpgrade(); | ||
|
||
Reset(); | ||
} | ||
|
||
[TearDown] | ||
public override void Shutdown() | ||
{ | ||
Reset(); | ||
|
||
base.Shutdown(); | ||
} | ||
|
||
protected abstract void Reset(); | ||
|
||
[Test] | ||
public void SimpleAcquireShouldWorkProperly() | ||
{ | ||
var databaseLock = GetService<IDatabaseLock>(); | ||
|
||
var acquried = databaseLock.Acquire(TestLockId, "Unit test", 5); | ||
Assert.That(acquried, Is.True); | ||
} | ||
|
||
[Test] | ||
public void SimpleReleaseShouldWorkProperly() | ||
{ | ||
var databaseLock = GetService<IDatabaseLock>(); | ||
|
||
Assert.DoesNotThrow(() => databaseLock.Release(TestLockId)); | ||
} | ||
|
||
[Test] | ||
public void NestedLockShouldNotAcquireAlreadyAcquiredLock() | ||
{ | ||
var databaseLock = GetService<IDatabaseLock>(); | ||
|
||
var acquried = databaseLock.Acquire(TestLockId, "Unit test", 5); | ||
Assert.That(acquried, Is.True); | ||
|
||
var nestedAcquire = databaseLock.Acquire(TestLockId, "Unit test", 5); | ||
Assert.That(nestedAcquire, Is.False); | ||
} | ||
|
||
[Test] | ||
public void SubsequentAcquireAndReleaseShouldWorkProperly() | ||
{ | ||
var databaseLock = GetService<IDatabaseLock>(); | ||
|
||
var acquried = databaseLock.Acquire(TestLockId, "Unit test", 5); | ||
Assert.That(acquried, Is.True); | ||
databaseLock.Release(TestLockId); | ||
|
||
var subsequentAcquire = databaseLock.Acquire(TestLockId, "Unit test", 5); | ||
Assert.That(subsequentAcquire, Is.True); | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using System; | ||
using System.Threading; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace DbKeeperNet.Engine | ||
{ | ||
public class DatabaseLockService : IDatabaseLockService | ||
{ | ||
private readonly ILogger<DatabaseLockService> _logger; | ||
private readonly IDatabaseLock _databaseLock; | ||
|
||
public DatabaseLockService(ILogger<DatabaseLockService> logger, IDatabaseLock databaseLock) | ||
{ | ||
_logger = logger; | ||
_databaseLock = databaseLock; | ||
} | ||
|
||
public IDisposable AcquireLock(int lockId) | ||
{ | ||
if (!_databaseLock.IsSupported) | ||
{ | ||
_logger.LogInformation("Database lock is not supported, assuming that lock {0} is acquired", lockId); | ||
return new DisposeAction(() => { }); | ||
} | ||
|
||
const int expirationMinutes = 5; | ||
|
||
while (!_databaseLock.Acquire(lockId, Guid.NewGuid().ToString(), expirationMinutes)) | ||
{ | ||
_logger.LogInformation("Database lock {0} could not be acquired - going to try again after 5 seconds", lockId); | ||
Thread.Sleep(5000); | ||
} | ||
|
||
_logger.LogInformation("Database lock {0} was acquired", lockId); | ||
|
||
return new DisposeAction( | ||
() => | ||
{ | ||
_databaseLock.Release(lockId); | ||
|
||
_logger.LogInformation("Database lock {0} was released", lockId); | ||
} | ||
); | ||
} | ||
|
||
private class DisposeAction : IDisposable | ||
{ | ||
private readonly Action _action; | ||
|
||
public DisposeAction(Action action) | ||
{ | ||
_action = action; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_action(); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.