From 3ce3560475daf1adc417a15a715d6880b2f16a5f Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Tue, 30 Mar 2021 21:42:10 +0200 Subject: [PATCH] Version fix. --- .../DomainObjectState.cs | 2 + .../Commands/DomainObject.cs | 32 ++++++---------- .../Commands/IDomainState.cs | 2 - .../Commands/SnapshotList.cs | 4 +- .../Orleans/GrainState.cs | 2 +- .../States/IPersistenceFactory.cs | 2 +- .../States/Persistence.cs | 14 +++---- .../TestHelpers/HandlerTestBase.cs | 2 +- .../Commands/DomainObjectTests.cs | 38 +++++++++---------- .../States/Save.cs | 2 +- .../TestHelpers/MyDomainState.cs | 2 - 11 files changed, 45 insertions(+), 57 deletions(-) diff --git a/backend/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs b/backend/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs index b0560434a..d5ad126ca 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs @@ -73,6 +73,8 @@ namespace Squidex.Domain.Apps.Entities clone.LastModified = timestamp; clone.LastModifiedBy = payload.Actor; + clone.Version++; + return (clone as T)!; } } diff --git a/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs b/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs index 0d5a9c0df..3e99df63e 100644 --- a/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs +++ b/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs @@ -61,23 +61,19 @@ namespace Squidex.Infrastructure.Commands if (result == null && valid) { - var snapshot = new T - { - Version = EtagVersion.Empty - }; + var snapshotCurrent = new T(); + var snapshotVersion = EtagVersion.Empty; - snapshots.Add(snapshot, snapshot.Version, false); + snapshots.Add(snapshotCurrent, snapshotVersion, false); var allEvents = factory.WithEventSourcing(GetType(), UniqueId, @event => { - var newVersion = snapshot.Version + 1; + snapshotVersion++; - if (!snapshots.Contains(newVersion)) + if (!snapshots.Contains(snapshotVersion)) { - snapshot = Apply(snapshot, @event); - snapshot.Version = newVersion; - - snapshots.Add(snapshot, snapshot.Version, false); + snapshotCurrent = Apply(snapshotCurrent, @event); + snapshots.Add(snapshotCurrent, snapshotVersion, false); return true; } @@ -90,7 +86,7 @@ namespace Squidex.Infrastructure.Commands (result, valid) = snapshots.Get(version); } - return result ?? new T { Version = EtagVersion.Empty }; + return result ?? new T(); } public virtual void Setup(DomainId uniqueId) @@ -98,10 +94,9 @@ namespace Squidex.Infrastructure.Commands this.uniqueId = uniqueId; persistence = factory.WithSnapshotsAndEventSourcing(GetType(), UniqueId, - new HandleSnapshot(snapshot => + new HandleSnapshot((snapshot, version) => { - snapshot.Version = Version + 1; - snapshots.Add(snapshot, snapshot.Version, true); + snapshots.Add(snapshot, version, true); }), @event => ApplyEvent(@event, true)); } @@ -292,14 +287,11 @@ namespace Squidex.Infrastructure.Commands private bool ApplyEvent(Envelope @event, bool isLoading) { - var newVersion = Version + 1; - var snapshotNew = Apply(Snapshot, @event); if (!ReferenceEquals(Snapshot, snapshotNew) || isLoading) { - snapshotNew.Version = newVersion; - snapshots.Add(snapshotNew, snapshotNew.Version, true); + snapshots.Add(snapshotNew, Version + 1, true); return true; } @@ -328,7 +320,7 @@ namespace Squidex.Infrastructure.Commands { await EnsureLoadedAsync(true); - if (Snapshot.Version <= EtagVersion.Empty) + if (Version <= EtagVersion.Empty) { throw new DomainObjectNotFoundException(UniqueId.ToString()); } diff --git a/backend/src/Squidex.Infrastructure/Commands/IDomainState.cs b/backend/src/Squidex.Infrastructure/Commands/IDomainState.cs index 9d4412f7c..a23f47079 100644 --- a/backend/src/Squidex.Infrastructure/Commands/IDomainState.cs +++ b/backend/src/Squidex.Infrastructure/Commands/IDomainState.cs @@ -11,8 +11,6 @@ namespace Squidex.Infrastructure.Commands { public interface IDomainState { - long Version { get; set; } - T Apply(Envelope @event); } } diff --git a/backend/src/Squidex.Infrastructure/Commands/SnapshotList.cs b/backend/src/Squidex.Infrastructure/Commands/SnapshotList.cs index cfe42373f..36c5e72d9 100644 --- a/backend/src/Squidex.Infrastructure/Commands/SnapshotList.cs +++ b/backend/src/Squidex.Infrastructure/Commands/SnapshotList.cs @@ -50,7 +50,7 @@ namespace Squidex.Infrastructure.Commands public void Clear() { items.Clear(); - items.Add(new T { Version = EtagVersion.Empty }); + items.Add(new T()); } public (T?, bool Valid) Get(long version) @@ -110,7 +110,7 @@ namespace Squidex.Infrastructure.Commands { var lastIndex = items.Count - 1; - for (var i = lastIndex - capacity; i >= 0; i--) + for (var i = lastIndex - capacity; i > 0; i--) { items[i] = null; } diff --git a/backend/src/Squidex.Infrastructure/Orleans/GrainState.cs b/backend/src/Squidex.Infrastructure/Orleans/GrainState.cs index 6c55c36b4..5808c7305 100644 --- a/backend/src/Squidex.Infrastructure/Orleans/GrainState.cs +++ b/backend/src/Squidex.Infrastructure/Orleans/GrainState.cs @@ -62,7 +62,7 @@ namespace Squidex.Infrastructure.Orleans return persistence.ReadAsync(); } - private void ApplyState(T value) + private void ApplyState(T value, long version) { Value = value; } diff --git a/backend/src/Squidex.Infrastructure/States/IPersistenceFactory.cs b/backend/src/Squidex.Infrastructure/States/IPersistenceFactory.cs index 8bd595603..5a75d737a 100644 --- a/backend/src/Squidex.Infrastructure/States/IPersistenceFactory.cs +++ b/backend/src/Squidex.Infrastructure/States/IPersistenceFactory.cs @@ -12,7 +12,7 @@ namespace Squidex.Infrastructure.States { public delegate bool HandleEvent(Envelope @event); - public delegate void HandleSnapshot(T state); + public delegate void HandleSnapshot(T state, long version); public interface IPersistenceFactory { diff --git a/backend/src/Squidex.Infrastructure/States/Persistence.cs b/backend/src/Squidex.Infrastructure/States/Persistence.cs index 4212864a8..f87f9eecf 100644 --- a/backend/src/Squidex.Infrastructure/States/Persistence.cs +++ b/backend/src/Squidex.Infrastructure/States/Persistence.cs @@ -109,17 +109,16 @@ namespace Squidex.Infrastructure.States private async Task ReadSnapshotAsync() { - var (state, position) = await snapshotStore.ReadAsync(ownerKey); + var (state, version) = await snapshotStore.ReadAsync(ownerKey); - // Treat all negative values as not-found (empty). - position = Math.Max(position, EtagVersion.Empty); + version = Math.Max(version, EtagVersion.Empty); - versionSnapshot = position; - versionEvents = position; + versionSnapshot = version; + versionEvents = version; - if (applyState != null && position >= 0) + if (applyState != null && version >= 0) { - applyState(state); + applyState(state, version); } } @@ -138,7 +137,6 @@ namespace Squidex.Infrastructure.States throw new InvalidOperationException("Events must follow the snapshot version in consecutive order with no gaps."); } - // Skip the parsing for performance reasons if we are not interested, but continue reading to get the version if (!isStopped) { var parsedEvent = eventDataFormatter.ParseIfKnown(@event); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs index 8d53a04fa..9d49f75e5 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs @@ -88,7 +88,7 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers return context; } - protected async Task PublishIdempotentAsync(DomainObject domainObject, IAggregateCommand command) where T : class, IDomainState, new() + protected async Task PublishIdempotentAsync(DomainObject domainObject, IAggregateCommand command) where T : class, IDomainState, IEntityWithVersion, new() { var result = await domainObject.ExecuteAsync(command); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectTests.cs index b9b2efc2f..d97496115 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectTests.cs @@ -32,7 +32,7 @@ namespace Squidex.Infrastructure.Commands public void Should_instantiate() { Assert.Equal(EtagVersion.Empty, sut.Version); - AssertSnapshot(sut.Snapshot, 0, -1); + AssertSnapshot(sut.Snapshot, 0); } [Fact] @@ -52,7 +52,7 @@ namespace Squidex.Infrastructure.Commands Assert.Equal(CommandResult.Empty(id, 0, EtagVersion.Empty), result); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(sut.Snapshot, 4, 0); + AssertSnapshot(sut.Snapshot, 4); } [Fact] @@ -76,7 +76,7 @@ namespace Squidex.Infrastructure.Commands Assert.Equal(CommandResult.Empty(id, 2, 1), result); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(sut.Snapshot, 4, 2); + AssertSnapshot(sut.Snapshot, 4); } [Fact] @@ -114,7 +114,7 @@ namespace Squidex.Infrastructure.Commands Assert.Equal(CommandResult.Empty(id, 2, 1), result); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(sut.Snapshot, 4, 2); + AssertSnapshot(sut.Snapshot, 4); } [Fact] @@ -150,7 +150,7 @@ namespace Squidex.Infrastructure.Commands Assert.Equal(CommandResult.Empty(id, 1, 0), result); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(sut.Snapshot, 8, 1); + AssertSnapshot(sut.Snapshot, 8); } [Fact] @@ -170,7 +170,7 @@ namespace Squidex.Infrastructure.Commands Assert.Equal(CommandResult.Empty(id, 1, 0), result); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(sut.Snapshot, 8, 1); + AssertSnapshot(sut.Snapshot, 8); } [Fact] @@ -196,7 +196,7 @@ namespace Squidex.Infrastructure.Commands .MustHaveHappenedOnceExactly(); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(sut.Snapshot, 9, 2); + AssertSnapshot(sut.Snapshot, 9); } [Fact] @@ -301,7 +301,7 @@ namespace Squidex.Infrastructure.Commands Assert.Equal(CommandResult.Empty(id, 0, 0), result); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(sut.Snapshot, 4, 0); + AssertSnapshot(sut.Snapshot, 4); } [Fact] @@ -315,7 +315,7 @@ namespace Squidex.Infrastructure.Commands await Assert.ThrowsAsync(() => sut.ExecuteAsync(new CreateAuto())); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(sut.Snapshot, 0, -1); + AssertSnapshot(sut.Snapshot, 0); } [Fact] @@ -329,7 +329,7 @@ namespace Squidex.Infrastructure.Commands await Assert.ThrowsAsync(() => sut.ExecuteAsync(new UpdateAuto())); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(sut.Snapshot, 4, 0); + AssertSnapshot(sut.Snapshot, 4); } [Fact] @@ -345,7 +345,7 @@ namespace Squidex.Infrastructure.Commands await sut.ExecuteAsync(new DeletePermanent()); - AssertSnapshot(sut.Snapshot, 0, EtagVersion.Empty, false); + AssertSnapshot(sut.Snapshot, 0, false); A.CallTo(() => persistence.DeleteAsync()) .MustHaveHappened(); @@ -372,9 +372,9 @@ namespace Squidex.Infrastructure.Commands var version_1 = await sut.GetSnapshotAsync(1); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(version_Empty, 0, -1); - AssertSnapshot(version_0, 3, 0); - AssertSnapshot(version_1, 4, 1); + AssertSnapshot(version_Empty, 0); + AssertSnapshot(version_0, 3); + AssertSnapshot(version_1, 4); A.CallTo(() => persistenceFactory.WithEventSourcing(typeof(MyDomainObject), id, A._)) .MustNotHaveHappened(); @@ -396,17 +396,17 @@ namespace Squidex.Infrastructure.Commands var version_1 = await sut.GetSnapshotAsync(1); Assert.Empty(sut.GetUncomittedEvents()); - AssertSnapshot(version_Empty, 0, -1); - AssertSnapshot(version_0, 3, 0); - AssertSnapshot(version_1, 4, 1); + AssertSnapshot(version_Empty, 0); + AssertSnapshot(version_0, 3); + AssertSnapshot(version_1, 4); A.CallTo(() => persistenceFactory.WithEventSourcing(typeof(MyDomainObject), id, A._)) .MustHaveHappened(); } - private static void AssertSnapshot(MyDomainState state, int value, long version, bool isDeleted = false) + private static void AssertSnapshot(MyDomainState state, int value, bool isDeleted = false) { - Assert.Equal(new MyDomainState { Value = value, Version = version, IsDeleted = isDeleted }, state); + Assert.Equal(new MyDomainState { Value = value, IsDeleted = isDeleted }, state); } private void SetupDeleted() diff --git a/backend/tests/Squidex.Infrastructure.Tests/States/Save.cs b/backend/tests/Squidex.Infrastructure.Tests/States/Save.cs index e6da562d0..5311ea784 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/States/Save.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/States/Save.cs @@ -32,7 +32,7 @@ namespace Squidex.Infrastructure.States { Value = initial; - Write = state => + Write = (state, _) => { Value = state; }; diff --git a/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs b/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs index 5a51eff9b..964ac2b12 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs @@ -16,8 +16,6 @@ namespace Squidex.Infrastructure.TestHelpers public bool IsDeleted { get; set; } - public long Version { get; set; } - public long Value { get; set; } public MyDomainState Apply(Envelope @event)