From 6186838f4ffce5082e41ce4ec3d9e789996e01eb Mon Sep 17 00:00:00 2001 From: stidsborg Date: Sat, 11 Jan 2025 09:05:23 +0100 Subject: [PATCH] Removed legacy distributed lock --- .../InMemoryTests/MonitorTests.cs | 56 ------- .../InMemoryTests/UtilsTests/MonitorTests.cs | 44 ------ .../UtilsTests/MonitorUnitTests.cs | 126 ---------------- .../TestTemplates/UtilsTests/MonitorTests.cs | 140 ------------------ .../CoreRuntime/Invocation/Utilities.cs | 4 +- .../Domain/DistributedSemaphore.cs | 3 + .../Storage/InMemoryFunctionStore.cs | 2 - .../Utils/Cemaphore/ICemaphore.cs | 20 --- .../Utils/Monitor/AcquiredLocks.cs | 18 --- .../Utils/Monitor/IMonitor.cs | 54 ------- .../Utils/Monitor/LockInfo.cs | 5 - .../Utils/Monitor/Monitor.cs | 49 ------ .../BankTransfer/Locking/TransferFunds.cs | 16 +- .../SharedResource/ISharedResourceApis.cs | 13 -- .../SharedResource/MonitorExample.cs | 34 ----- .../Subscription/SubscriptionSaga.cs | 10 +- .../UtilTests/MonitorTests.cs | 47 ------ .../UtilTests/MonitorTests.cs | 48 ------ .../UtilTests/MonitorTests.cs | 47 ------ 19 files changed, 12 insertions(+), 724 deletions(-) delete mode 100644 Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/MonitorTests.cs delete mode 100644 Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/UtilsTests/MonitorTests.cs delete mode 100644 Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/UtilsTests/MonitorUnitTests.cs delete mode 100644 Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/UtilsTests/MonitorTests.cs delete mode 100644 Core/Cleipnir.ResilientFunctions/Utils/Cemaphore/ICemaphore.cs delete mode 100644 Core/Cleipnir.ResilientFunctions/Utils/Monitor/AcquiredLocks.cs delete mode 100644 Core/Cleipnir.ResilientFunctions/Utils/Monitor/IMonitor.cs delete mode 100644 Core/Cleipnir.ResilientFunctions/Utils/Monitor/LockInfo.cs delete mode 100644 Core/Cleipnir.ResilientFunctions/Utils/Monitor/Monitor.cs delete mode 100644 Samples/Sample.ConsoleApp/SharedResource/ISharedResourceApis.cs delete mode 100644 Samples/Sample.ConsoleApp/SharedResource/MonitorExample.cs delete mode 100644 Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB.Tests/UtilTests/MonitorTests.cs delete mode 100644 Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL.Tests/UtilTests/MonitorTests.cs delete mode 100644 Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer.Tests/UtilTests/MonitorTests.cs diff --git a/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/MonitorTests.cs b/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/MonitorTests.cs deleted file mode 100644 index dc027537..00000000 --- a/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/MonitorTests.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Cleipnir.ResilientFunctions.Helpers; -using Cleipnir.ResilientFunctions.Utils; -using Cleipnir.ResilientFunctions.Utils.Monitor; -using Cleipnir.ResilientFunctions.Utils.Register; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Monitor = Cleipnir.ResilientFunctions.Utils.Monitor.Monitor; - -namespace Cleipnir.ResilientFunctions.Tests.InMemoryTests; - -[TestClass] -public class MonitorTests : TestTemplates.UtilsTests.MonitorTests -{ - private readonly Dictionary _monitors = new(); - private readonly Lock _sync = new(); - - [TestMethod] - public override Task LockCanBeAcquiredAndReleasedSuccessfully() - => LockCanBeAcquiredAndReleasedSuccessfully(CreateInMemoryMonitor()); - - [TestMethod] - public override Task TwoDifferentLocksCanBeAcquired() - => TwoDifferentLocksCanBeAcquired(CreateInMemoryMonitor()); - - [TestMethod] - public override Task TakingATakenLockFails() - => TakingATakenLockFails(CreateInMemoryMonitor()); - - [TestMethod] - public override Task ReTakingATakenLockWithSameKeyIdSucceeds() - => ReTakingATakenLockWithSameKeyIdSucceeds(CreateInMemoryMonitor()); - - [TestMethod] - public override Task AReleasedLockCanBeTakenAgain() - => AReleasedLockCanBeTakenAgain(CreateInMemoryMonitor()); - - [TestMethod] - public override Task WaitingAboveThresholdForATakenLockReturnsNull() - => WaitingAboveThresholdForATakenLockReturnsNull(CreateInMemoryMonitor()); - - [TestMethod] - public override Task WhenALockIsReleasedActiveAcquireShouldGetTheLock() - => WhenALockIsReleasedActiveAcquireShouldGetTheLock(CreateInMemoryMonitor()); - - private Task CreateInMemoryMonitor([CallerMemberName] string memberName = "") - { - var monitor = new Monitor(new UnderlyingInMemoryRegister()); - lock (_sync) - _monitors[memberName] = monitor; - - return monitor.CastTo().ToTask(); - } -} \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/UtilsTests/MonitorTests.cs b/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/UtilsTests/MonitorTests.cs deleted file mode 100644 index 1614db48..00000000 --- a/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/UtilsTests/MonitorTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Threading.Tasks; -using Cleipnir.ResilientFunctions.Helpers; -using Cleipnir.ResilientFunctions.Utils; -using Cleipnir.ResilientFunctions.Utils.Monitor; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Cleipnir.ResilientFunctions.Tests.InMemoryTests.UtilsTests; - -[TestClass] -public class MonitorTests : Cleipnir.ResilientFunctions.Tests.TestTemplates.UtilsTests.MonitorTests -{ - private Monitor _monitor = new(new UnderlyingInMemoryRegister()); - - [TestInitialize] - public void Initialize() => _monitor = new Monitor(new UnderlyingInMemoryRegister()); - - [TestMethod] - public override Task LockCanBeAcquiredAndReleasedSuccessfully() - => LockCanBeAcquiredAndReleasedSuccessfully(_monitor.CastTo().ToTask()); - - [TestMethod] - public override Task TwoDifferentLocksCanBeAcquired() - => TwoDifferentLocksCanBeAcquired(_monitor.CastTo().ToTask()); - - [TestMethod] - public override Task TakingATakenLockFails() - => TakingATakenLockFails(_monitor.CastTo().ToTask()); - - [TestMethod] - public override Task ReTakingATakenLockWithSameKeyIdSucceeds() - => ReTakingATakenLockWithSameKeyIdSucceeds(_monitor.CastTo().ToTask()); - - [TestMethod] - public override Task AReleasedLockCanBeTakenAgain() - => AReleasedLockCanBeTakenAgain(_monitor.CastTo().ToTask()); - - [TestMethod] - public override Task WaitingAboveThresholdForATakenLockReturnsNull() - => WaitingAboveThresholdForATakenLockReturnsNull(_monitor.CastTo().ToTask()); - - [TestMethod] - public override Task WhenALockIsReleasedActiveAcquireShouldGetTheLock() - => WhenALockIsReleasedActiveAcquireShouldGetTheLock(_monitor.CastTo().ToTask()); -} \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/UtilsTests/MonitorUnitTests.cs b/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/UtilsTests/MonitorUnitTests.cs deleted file mode 100644 index 278b80cd..00000000 --- a/Core/Cleipnir.ResilientFunctions.Tests/InMemoryTests/UtilsTests/MonitorUnitTests.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Cleipnir.ResilientFunctions.Tests.Utils; -using Cleipnir.ResilientFunctions.Utils; -using Cleipnir.ResilientFunctions.Utils.Monitor; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Shouldly; - -namespace Cleipnir.ResilientFunctions.Tests.InMemoryTests.UtilsTests; - -[TestClass] -public class MonitorUnitTests -{ - [TestMethod] - public async Task MultipleLocksCanBeAcquiredInSortedOrderOnName() - { - var callbacks = new List>(); - - IMonitor monitor = new Monitor(new DecoratedRegister( - compareAndSwapCallback: (group, name, lockId, success) => callbacks.Add(Tuple.Create(group, name, lockId, success))) - ); - - var acquiredLocks = await monitor.Acquire( - new LockInfo("SomeGroup", Name: "B", "someLockId"), - new LockInfo("SomeGroup", Name: "A", "someLockId") - ); - - callbacks.Count.ShouldBe(2); - callbacks[0].Item2.ShouldBe("A"); - callbacks[1].Item2.ShouldBe("B"); - callbacks.All(t => t.Item4).ShouldBeTrue(); - - acquiredLocks.ShouldNotBeNull(); - await acquiredLocks.DisposeAsync(); - - //check locks are free after dispose - await monitor.Acquire("SomeGroup", "B", "someOtherLockId").ShouldNotBeNullAsync(); - await monitor.Acquire("SomeGroup", "A", "someOtherLockId").ShouldNotBeNullAsync(); - } - - [TestMethod] - public async Task MultipleLocksCanBeAcquiredInSortedOrderOnGroup() - { - var callbacks = new List>(); - - IMonitor monitor = new Monitor(new DecoratedRegister( - compareAndSwapCallback: (group, name, lockId, success) => callbacks.Add(Tuple.Create(group, name, lockId, success))) - ); - - var acquiredLocks = await monitor.Acquire( - new LockInfo(GroupId: "B", "SomeName", "someLockId"), - new LockInfo(GroupId: "A", "SomeName", "someLockId") - ); - - callbacks.Count.ShouldBe(2); - callbacks[0].Item1.ShouldBe("A"); - callbacks[1].Item1.ShouldBe("B"); - callbacks.All(t => t.Item4).ShouldBeTrue(); - - acquiredLocks.ShouldNotBeNull(); - await acquiredLocks.DisposeAsync(); - - //check locks are free after dispose - await monitor.Acquire("B", "SomeName", "someOtherLockId").ShouldNotBeNullAsync(); - await monitor.Acquire("A", "SomeName", "someOtherLockId").ShouldNotBeNullAsync(); - } - - [TestMethod] - public async Task PreviousAcquiredLockIsReleasedWhenLaterLockCannotBeAcquired() - { - IMonitor monitor = new Monitor(new DecoratedRegister(compareAndSwapCallback: (_, _, _, _) => {})); - - await monitor.Acquire("A", "SomeName", "someOtherLockId"); - - await monitor.Acquire( - new LockInfo(GroupId: "B", "SomeName", "someLockId"), - new LockInfo(GroupId: "A", "SomeName", "someLockId") - ); - - await monitor.Acquire("B", "SomeName", "someOtherLockId").ShouldNotBeNullAsync(); - } - - private class DecoratedRegister : IUnderlyingRegister - { - private readonly Action _compareAndSwapCallback; - private readonly IUnderlyingRegister _inner = new UnderlyingInMemoryRegister(); - - public DecoratedRegister(Action compareAndSwapCallback) - => _compareAndSwapCallback = compareAndSwapCallback; - - public Task SetIfEmpty(RegisterType registerType, string group, string name, string value) - { - return _inner.SetIfEmpty(registerType, group, name, value); - } - - public async Task CompareAndSwap(RegisterType registerType, string group, string name, string newValue, string expectedValue, bool setIfEmpty = true) - { - var success = await _inner.CompareAndSwap(registerType, group, name, newValue, expectedValue, setIfEmpty); - _compareAndSwapCallback(group, name, newValue, success); - - return success; - } - - public Task Get(RegisterType registerType, string group, string name) - { - return _inner.Get(registerType, group, name); - } - - public Task Delete(RegisterType registerType, string group, string name, string expectedValue) - { - return _inner.Delete(registerType, group, name, expectedValue); - } - - public Task Delete(RegisterType registerType, string group, string name) - { - return _inner.Delete(registerType, group, name); - } - - public Task Exists(RegisterType registerType, string group, string name) - { - return _inner.Exists(registerType, group, name); - } - } -} \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/UtilsTests/MonitorTests.cs b/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/UtilsTests/MonitorTests.cs deleted file mode 100644 index d2fe6dbc..00000000 --- a/Core/Cleipnir.ResilientFunctions.Tests/TestTemplates/UtilsTests/MonitorTests.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Cleipnir.ResilientFunctions.Tests.Utils; -using Cleipnir.ResilientFunctions.Utils.Monitor; -using Shouldly; - -namespace Cleipnir.ResilientFunctions.Tests.TestTemplates.UtilsTests; - -public abstract class MonitorTests -{ - public abstract Task LockCanBeAcquiredAndReleasedSuccessfully(); - protected async Task LockCanBeAcquiredAndReleasedSuccessfully(Task monitorTask) - { - var monitor = await monitorTask; - const string instance = "123"; - const string lockId = "321"; - - var @lock = await monitor.Acquire(group: nameof(LockCanBeAcquiredAndReleasedSuccessfully),instance, lockId); - @lock.ShouldNotBeNull(); - - var lock2 = await monitor.Acquire(group: nameof(LockCanBeAcquiredAndReleasedSuccessfully),instance, lockId: ""); - lock2.ShouldBeNull(); - - await @lock.DisposeAsync(); - - lock2 = await monitor.Acquire(group: nameof(LockCanBeAcquiredAndReleasedSuccessfully),instance, lockId: ""); - lock2.ShouldNotBeNull(); - } - - public abstract Task TwoDifferentLocksCanBeAcquired(); - protected async Task TwoDifferentLocksCanBeAcquired(Task monitorTask) - { - var monitor = await monitorTask; - const string instance1 = "123"; - const string lockId1 = "321"; - const string instance2 = "1234"; - const string lockId2 = "3210"; - - var lock1 = await monitor.Acquire(nameof(TwoDifferentLocksCanBeAcquired), instance1, lockId1); - lock1.ShouldNotBeNull(); - var lock2 = await monitor.Acquire(nameof(TwoDifferentLocksCanBeAcquired), instance2, lockId2); - lock2.ShouldNotBeNull(); - - await monitor.Acquire(nameof(TwoDifferentLocksCanBeAcquired), instance1, lockId: "").ShouldBeNullAsync(); - await monitor.Acquire(nameof(TwoDifferentLocksCanBeAcquired), instance2, lockId: "").ShouldBeNullAsync(); - - await lock1.DisposeAsync(); - await monitor.Acquire(nameof(TwoDifferentLocksCanBeAcquired), instance2, lockId: "").ShouldBeNullAsync(); - await lock2.DisposeAsync(); - - await monitor.Acquire(nameof(TwoDifferentLocksCanBeAcquired), instance1, lockId: "").ShouldNotBeNullAsync(); - await monitor.Acquire(nameof(TwoDifferentLocksCanBeAcquired), instance2, lockId: "").ShouldNotBeNullAsync(); - } - - public abstract Task TakingATakenLockFails(); - protected async Task TakingATakenLockFails(Task monitorTask) - { - var monitor = await monitorTask; - const string instance = "123"; - const string lockId1 = "321"; - const string lockId2 = "3210"; - - var @lock = await monitor.Acquire(nameof(TakingATakenLockFails), instance, lockId1); - @lock.ShouldNotBeNull(); - - await monitor.Acquire(nameof(TakingATakenLockFails), instance, lockId2).ShouldBeNullAsync(); - - await @lock.DisposeAsync(); - await monitor.Acquire(nameof(TakingATakenLockFails), instance, lockId2).ShouldNotBeNullAsync(); - } - - public abstract Task ReTakingATakenLockWithSameKeyIdSucceeds(); - protected async Task ReTakingATakenLockWithSameKeyIdSucceeds(Task monitorTask) - { - var monitor = await monitorTask; - var group = Guid.NewGuid().ToString(); - const string lockName = "123"; - const string key = "321"; - - var @lock = await monitor.Acquire(group, lockName, key); - @lock.ShouldNotBeNull(); - - var lock2 = await monitor.Acquire(group, lockName, key); - lock2.ShouldNotBeNull(); - - await @lock.DisposeAsync(); - - var lock3 = await monitor.Acquire(group, lockName, key); - lock3.ShouldNotBeNull(); - } - - public abstract Task AReleasedLockCanBeTakenAgain(); - protected async Task AReleasedLockCanBeTakenAgain(Task monitorTask) - { - var monitor = await monitorTask; - var group = Guid.NewGuid().ToString(); - const string lockName = "123"; - const string keyId = "321"; - - var lock1 = await monitor.Acquire(group, lockName, keyId); - lock1.ShouldNotBeNull(); - await lock1.DisposeAsync(); - - var lock2 = await monitor.Acquire(group, lockName, keyId); - lock2.ShouldNotBeNull(); - } - - public abstract Task WaitingAboveThresholdForATakenLockReturnsNull(); - protected async Task WaitingAboveThresholdForATakenLockReturnsNull(Task monitorTask) - { - var monitor = await monitorTask; - var group = Guid.NewGuid().ToString(); - const string lockName = "123"; - const string key1Id = "321"; - const string key2Id = "3210"; - - await monitor.Acquire(group, lockName, key1Id); - await monitor.Acquire(group, lockName, key2Id, 250).ShouldBeNullAsync(); - } - - public abstract Task WhenALockIsReleasedActiveAcquireShouldGetTheLock(); - protected async Task WhenALockIsReleasedActiveAcquireShouldGetTheLock(Task monitorTask) - { - var monitor = await monitorTask; - var group = Guid.NewGuid().ToString(); - const string lockName = "123"; - const string key1Id = "321"; - const string key2Id = "3210"; - - var @lock = await monitor.Acquire(group, lockName, key1Id); - @lock.ShouldNotBeNull(); - var acquireTask = monitor.Acquire(group, lockName, key2Id, TimeSpan.FromSeconds(5)); - await Task.Delay(150); - await @lock.DisposeAsync(); - - var lock2 = await acquireTask; - lock2.ShouldNotBeNull(); - } -} \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/CoreRuntime/Invocation/Utilities.cs b/Core/Cleipnir.ResilientFunctions/CoreRuntime/Invocation/Utilities.cs index 6e50d1fb..cadeb622 100644 --- a/Core/Cleipnir.ResilientFunctions/CoreRuntime/Invocation/Utilities.cs +++ b/Core/Cleipnir.ResilientFunctions/CoreRuntime/Invocation/Utilities.cs @@ -1,15 +1,13 @@ using Cleipnir.ResilientFunctions.Utils; using Cleipnir.ResilientFunctions.Utils.Arbitrator; -using Cleipnir.ResilientFunctions.Utils.Monitor; using Cleipnir.ResilientFunctions.Utils.Register; namespace Cleipnir.ResilientFunctions.CoreRuntime.Invocation; -public record Utilities(IMonitor Monitor, IRegister Register, IArbitrator Arbitrator) +public record Utilities(IRegister Register, IArbitrator Arbitrator) { public Utilities(IUnderlyingRegister underlyingRegister) : this( - new Monitor(underlyingRegister), new Register(underlyingRegister), new Arbitrator(underlyingRegister) ) {} diff --git a/Core/Cleipnir.ResilientFunctions/Domain/DistributedSemaphore.cs b/Core/Cleipnir.ResilientFunctions/Domain/DistributedSemaphore.cs index e4e75a17..a02850d7 100644 --- a/Core/Cleipnir.ResilientFunctions/Domain/DistributedSemaphore.cs +++ b/Core/Cleipnir.ResilientFunctions/Domain/DistributedSemaphore.cs @@ -12,6 +12,9 @@ public class DistributedSemaphores(Effect effect, ISemaphoreStore semaphoreStore { public DistributedSemaphore Create(string group, string instance, int maximumCount) => new(maximumCount, group, instance, effect, semaphoreStore, storedId, interrupt); + + public DistributedSemaphore CreateLock(string group, string instance) + => new(maximumCount: 1, group, instance, effect, semaphoreStore, storedId, interrupt); } public class DistributedSemaphore(int maximumCount, string group, string instance, Effect effect, ISemaphoreStore store, StoredId storedId, Func, Task> interrupt) diff --git a/Core/Cleipnir.ResilientFunctions/Storage/InMemoryFunctionStore.cs b/Core/Cleipnir.ResilientFunctions/Storage/InMemoryFunctionStore.cs index 475fed11..bcaef125 100644 --- a/Core/Cleipnir.ResilientFunctions/Storage/InMemoryFunctionStore.cs +++ b/Core/Cleipnir.ResilientFunctions/Storage/InMemoryFunctionStore.cs @@ -9,7 +9,6 @@ using Cleipnir.ResilientFunctions.Utils; using Cleipnir.ResilientFunctions.Utils.Arbitrator; using Cleipnir.ResilientFunctions.Utils.Register; -using Monitor = Cleipnir.ResilientFunctions.Utils.Monitor.Monitor; namespace Cleipnir.ResilientFunctions.Storage; @@ -38,7 +37,6 @@ public InMemoryFunctionStore() { var underlyingRegister = new UnderlyingInMemoryRegister(); Utilities = new Utilities( - new Monitor(underlyingRegister), new Register(underlyingRegister), new Arbitrator(underlyingRegister) ); diff --git a/Core/Cleipnir.ResilientFunctions/Utils/Cemaphore/ICemaphore.cs b/Core/Cleipnir.ResilientFunctions/Utils/Cemaphore/ICemaphore.cs deleted file mode 100644 index c1c1d750..00000000 --- a/Core/Cleipnir.ResilientFunctions/Utils/Cemaphore/ICemaphore.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Cleipnir.ResilientFunctions.Utils.Semaphore; - -public interface ISemaphore -{ - public Task Acquire(string group, string name, TimeSpan? maxWait = null); - public Task TryAcquire(TimeSpan? maxWait = null); - - public Task Release(string group, string name, string lockId); - - public interface ILock - { - public Task DoThenRelease(Action action); - public Task DoThenRelease(Func func); - public Task DoThenRelease(Func> func); - public Task Release(); - } -} \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/Utils/Monitor/AcquiredLocks.cs b/Core/Cleipnir.ResilientFunctions/Utils/Monitor/AcquiredLocks.cs deleted file mode 100644 index 3d005579..00000000 --- a/Core/Cleipnir.ResilientFunctions/Utils/Monitor/AcquiredLocks.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Cleipnir.ResilientFunctions.Utils.Monitor; - -public class AcquiredLocks : IAsyncDisposable -{ - private readonly IEnumerable _locks; - - internal AcquiredLocks(IEnumerable locks) => _locks = locks; - - public async ValueTask DisposeAsync() - { - foreach (var prevAcquiredLock in _locks) - await prevAcquiredLock.DisposeAsync(); - } -} \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/Utils/Monitor/IMonitor.cs b/Core/Cleipnir.ResilientFunctions/Utils/Monitor/IMonitor.cs deleted file mode 100644 index 72edb1a1..00000000 --- a/Core/Cleipnir.ResilientFunctions/Utils/Monitor/IMonitor.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Cleipnir.ResilientFunctions.Utils.Monitor; - -public interface IMonitor -{ - public Task Acquire(string group, string name, string lockId); - - public async Task Acquire(string group, string name, string lockId, TimeSpan maxWait) - { - var prev = DateTime.UtcNow; - while (true) - { - var @lock = await Acquire(group, name, lockId); - if (@lock != null) return @lock; - await Task.Delay(100); - if (DateTime.UtcNow - prev > maxWait) return null; - } - } - - public Task Acquire(string group, string name, string lockId, int maxWaitMs) - => Acquire(group, name, lockId, TimeSpan.FromMilliseconds(maxWaitMs)); - - public async Task Acquire(params LockInfo[] locks) - { - var orderedLocks = locks.OrderBy(r => r.GroupId).ThenBy(r => r.Name); - var acquiredLocks = new List(locks.Length); - foreach (var (groupId, name, lockId, timeSpan) in orderedLocks) - { - var acquiredLock = timeSpan == null - ? await Acquire(groupId, name, lockId) - : await Acquire(groupId, name, lockId, timeSpan.Value); - - if (acquiredLock == null) - { - foreach (var prevAcquiredLock in acquiredLocks) - await prevAcquiredLock.DisposeAsync(); - - return null; - } - - acquiredLocks.Add(acquiredLock); - } - - return new AcquiredLocks(acquiredLocks); - } - - public Task Release(string group, string name, string lockId); - - public interface ILock : IAsyncDisposable { } -} \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/Utils/Monitor/LockInfo.cs b/Core/Cleipnir.ResilientFunctions/Utils/Monitor/LockInfo.cs deleted file mode 100644 index 14fd4d57..00000000 --- a/Core/Cleipnir.ResilientFunctions/Utils/Monitor/LockInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System; - -namespace Cleipnir.ResilientFunctions.Utils.Monitor; - -public record LockInfo(string GroupId, string Name, string LockId, TimeSpan? MaxWait = null); \ No newline at end of file diff --git a/Core/Cleipnir.ResilientFunctions/Utils/Monitor/Monitor.cs b/Core/Cleipnir.ResilientFunctions/Utils/Monitor/Monitor.cs deleted file mode 100644 index b3383959..00000000 --- a/Core/Cleipnir.ResilientFunctions/Utils/Monitor/Monitor.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Threading.Tasks; - -namespace Cleipnir.ResilientFunctions.Utils.Monitor; - -public class Monitor : IMonitor -{ - private readonly IUnderlyingRegister _register; - - public Monitor(IUnderlyingRegister underlyingRegister) - { - _register = underlyingRegister; - } - - public async Task Acquire(string group, string name, string lockId) - { - var success = await _register.CompareAndSwap( - RegisterType.Monitor, - group, - name, - expectedValue: lockId, - newValue: lockId, - setIfEmpty: true - ); - if (!success) return null; - - return new Lock(this, group, name, lockId); - } - - public async Task Release(string group, string name, string lockId) - => await _register.Delete(RegisterType.Monitor, group, name, expectedValue: lockId); - - private class Lock : IMonitor.ILock - { - private readonly IMonitor _monitor; - private readonly string _group; - private readonly string _instance; - private readonly string _lockId; - - public Lock(IMonitor monitor, string group, string instance, string lockId) - { - _monitor = monitor; - _group = group; - _instance = instance; - _lockId = lockId; - } - - public async ValueTask DisposeAsync() => await _monitor.Release(_group, _instance, _lockId); - } -} \ No newline at end of file diff --git a/Samples/Sample.ConsoleApp/BankTransfer/Locking/TransferFunds.cs b/Samples/Sample.ConsoleApp/BankTransfer/Locking/TransferFunds.cs index 6d4e2f15..3de2808d 100644 --- a/Samples/Sample.ConsoleApp/BankTransfer/Locking/TransferFunds.cs +++ b/Samples/Sample.ConsoleApp/BankTransfer/Locking/TransferFunds.cs @@ -1,7 +1,5 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Cleipnir.ResilientFunctions.CoreRuntime.Invocation; -using Cleipnir.ResilientFunctions.Utils.Monitor; using ConsoleApp.BankTransfer.Versioning; namespace ConsoleApp.BankTransfer.Locking; @@ -12,13 +10,11 @@ public static class TransferFunds public static async Task Perform(Transfer transfer, Workflow workflow) { - var monitor = workflow.Utilities.Monitor; - - var lockId = await workflow.Effect.Capture("lockId", () => Guid.NewGuid().ToString()); - await using var _ = await monitor.Acquire( - new LockInfo("Account", transfer.FromAccount, lockId), - new LockInfo("Account", transfer.ToAccount, lockId) - ); + var fromAccount = workflow.Semaphores.CreateLock("Account", transfer.FromAccount); + var toAccount = workflow.Semaphores.CreateLock("Account", transfer.ToAccount); + + await using var fromAccountLock = await fromAccount.Acquire(); + await using var toAccountLock = await toAccount.Acquire(); var deductTask = workflow.Effect.Capture( "DeductAmount", diff --git a/Samples/Sample.ConsoleApp/SharedResource/ISharedResourceApis.cs b/Samples/Sample.ConsoleApp/SharedResource/ISharedResourceApis.cs deleted file mode 100644 index efb5e747..00000000 --- a/Samples/Sample.ConsoleApp/SharedResource/ISharedResourceApis.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Threading.Tasks; - -namespace ConsoleApp.SharedResource; - -public interface ISharedResourceApi1 -{ - public Task SetValue(string resourceId, string value); -} - -public interface ISharedResourceApi2 -{ - public Task SetValue(string resourceId, string value); -} \ No newline at end of file diff --git a/Samples/Sample.ConsoleApp/SharedResource/MonitorExample.cs b/Samples/Sample.ConsoleApp/SharedResource/MonitorExample.cs deleted file mode 100644 index c29cbed3..00000000 --- a/Samples/Sample.ConsoleApp/SharedResource/MonitorExample.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Threading.Tasks; -using Cleipnir.ResilientFunctions.Domain; -using Cleipnir.ResilientFunctions.Utils.Monitor; - -namespace ConsoleApp.SharedResource; - -public class MonitorExample -{ - private IMonitor Monitor { get; } - private ISharedResourceApi1 ResourceApi1 { get; } - private ISharedResourceApi2 ResourceApi2 { get; } - - public MonitorExample(IMonitor monitor, ISharedResourceApi1 resourceApi1, ISharedResourceApi2 resourceApi2) - { - Monitor = monitor; - ResourceApi1 = resourceApi1; - ResourceApi2 = resourceApi2; - } - - public async Task Invoke(string id, string resourceId, string value) - { - await using var @lock = await Monitor.Acquire( - group: nameof(MonitorExample), - name: "monitor", - lockId: id - ); - if (@lock == null) - return Postpone.For(10_000); - - await ResourceApi1.SetValue(resourceId, value); - await ResourceApi2.SetValue(resourceId, value); - return Succeed.WithoutValue; - } -} \ No newline at end of file diff --git a/Samples/Sample.ConsoleApp/Subscription/SubscriptionSaga.cs b/Samples/Sample.ConsoleApp/Subscription/SubscriptionSaga.cs index 1603293e..fae24e4b 100644 --- a/Samples/Sample.ConsoleApp/Subscription/SubscriptionSaga.cs +++ b/Samples/Sample.ConsoleApp/Subscription/SubscriptionSaga.cs @@ -9,15 +9,9 @@ public class SubscriptionSaga { public async Task UpdateSubscription(SubscriptionChange subscriptionChange, State state, Workflow workflow) { - var monitor = workflow.Utilities.Monitor; + var monitor = workflow.Semaphores.CreateLock(nameof(UpdateSubscription), subscriptionChange.SubscriptionId); var (subscriptionId, startSubscription) = subscriptionChange; - await using var @lock = await monitor.Acquire( - group: nameof(UpdateSubscription), - name: subscriptionChange.SubscriptionId, - lockId: state.LockId - ); - if (@lock == null) - return Postpone.For(10_000); + await monitor.Acquire(); if (startSubscription) await StartSubscription(subscriptionId); diff --git a/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB.Tests/UtilTests/MonitorTests.cs b/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB.Tests/UtilTests/MonitorTests.cs deleted file mode 100644 index 459b6d56..00000000 --- a/Stores/MariaDB/Cleipnir.ResilientFunctions.MariaDB.Tests/UtilTests/MonitorTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Runtime.CompilerServices; -using Cleipnir.ResilientFunctions.Utils.Monitor; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using MySqlConnector; -using Monitor = Cleipnir.ResilientFunctions.Utils.Monitor.Monitor; - -namespace Cleipnir.ResilientFunctions.MariaDb.Tests.UtilTests; - -[TestClass] -public class MonitorTests : ResilientFunctions.Tests.TestTemplates.UtilsTests.MonitorTests -{ - [TestMethod] - public override Task LockCanBeAcquiredAndReleasedSuccessfully() - => LockCanBeAcquiredAndReleasedSuccessfully(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task TwoDifferentLocksCanBeAcquired() - => TwoDifferentLocksCanBeAcquired(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task TakingATakenLockFails() - => TakingATakenLockFails(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task ReTakingATakenLockWithSameKeyIdSucceeds() - => ReTakingATakenLockWithSameKeyIdSucceeds(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task AReleasedLockCanBeTakenAgain() - => AReleasedLockCanBeTakenAgain(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task WaitingAboveThresholdForATakenLockReturnsNull() - => WaitingAboveThresholdForATakenLockReturnsNull(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task WhenALockIsReleasedActiveAcquireShouldGetTheLock() - => WhenALockIsReleasedActiveAcquireShouldGetTheLock(CreateAndInitializeMonitor()); - - private async Task CreateAndInitializeMonitor([CallerMemberName] string memberName = "") - { - var underlyingRegister = new MariaDbUnderlyingRegister(Sql.ConnectionString); - var monitor = new Monitor(underlyingRegister); - await underlyingRegister.Initialize(); - return monitor; - } -} \ No newline at end of file diff --git a/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL.Tests/UtilTests/MonitorTests.cs b/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL.Tests/UtilTests/MonitorTests.cs deleted file mode 100644 index 46901c82..00000000 --- a/Stores/PostgreSQL/Cleipnir.ResilientFunctions.PostgreSQL.Tests/UtilTests/MonitorTests.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Cleipnir.ResilientFunctions.Utils.Monitor; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Npgsql; - -namespace Cleipnir.ResilientFunctions.PostgreSQL.Tests.UtilTests; - -[TestClass] -public class MonitorTests : ResilientFunctions.Tests.TestTemplates.UtilsTests.MonitorTests -{ - [TestMethod] - public override Task LockCanBeAcquiredAndReleasedSuccessfully() - => LockCanBeAcquiredAndReleasedSuccessfully(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task TwoDifferentLocksCanBeAcquired() - => TwoDifferentLocksCanBeAcquired(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task TakingATakenLockFails() - => TakingATakenLockFails(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task ReTakingATakenLockWithSameKeyIdSucceeds() - => ReTakingATakenLockWithSameKeyIdSucceeds(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task AReleasedLockCanBeTakenAgain() - => AReleasedLockCanBeTakenAgain(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task WaitingAboveThresholdForATakenLockReturnsNull() - => WaitingAboveThresholdForATakenLockReturnsNull(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task WhenALockIsReleasedActiveAcquireShouldGetTheLock() - => WhenALockIsReleasedActiveAcquireShouldGetTheLock(CreateAndInitializeMonitor()); - - private async Task CreateAndInitializeMonitor([CallerMemberName] string memberName = "") - { - var underlyingRegister = new PostgresSqlUnderlyingRegister(Sql.ConnectionString); - var monitor = new Monitor(underlyingRegister); - await underlyingRegister.Initialize(); - await underlyingRegister.Initialize(); - return monitor; - } -} \ No newline at end of file diff --git a/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer.Tests/UtilTests/MonitorTests.cs b/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer.Tests/UtilTests/MonitorTests.cs deleted file mode 100644 index 4f59f26d..00000000 --- a/Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer.Tests/UtilTests/MonitorTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Cleipnir.ResilientFunctions.Utils.Monitor; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Cleipnir.ResilientFunctions.SqlServer.Tests.UtilTests; - -[TestClass] -public class MonitorTests : ResilientFunctions.Tests.TestTemplates.UtilsTests.MonitorTests -{ - [TestMethod] - public override Task LockCanBeAcquiredAndReleasedSuccessfully() - => LockCanBeAcquiredAndReleasedSuccessfully(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task TwoDifferentLocksCanBeAcquired() - => TwoDifferentLocksCanBeAcquired(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task TakingATakenLockFails() - => TakingATakenLockFails(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task ReTakingATakenLockWithSameKeyIdSucceeds() - => ReTakingATakenLockWithSameKeyIdSucceeds(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task AReleasedLockCanBeTakenAgain() - => AReleasedLockCanBeTakenAgain(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task WaitingAboveThresholdForATakenLockReturnsNull() - => WaitingAboveThresholdForATakenLockReturnsNull(CreateAndInitializeMonitor()); - - [TestMethod] - public override Task WhenALockIsReleasedActiveAcquireShouldGetTheLock() - => WhenALockIsReleasedActiveAcquireShouldGetTheLock(CreateAndInitializeMonitor()); - - private async Task CreateAndInitializeMonitor([CallerMemberName] string memberName = "") - { - var underlyingRegister = new SqlServerUnderlyingRegister(Sql.ConnectionString, tablePrefix: memberName); - var monitor = new Monitor(underlyingRegister); - await underlyingRegister.Initialize(); - await underlyingRegister.Initialize(); - return monitor; - } -} \ No newline at end of file