diff --git a/backend/src/Squidex.Infrastructure.MongoDb/Migrations/MongoMigrationStatus.cs b/backend/src/Squidex.Infrastructure.MongoDb/Migrations/MongoMigrationStatus.cs index 1fe6eb442..0b7ad480b 100644 --- a/backend/src/Squidex.Infrastructure.MongoDb/Migrations/MongoMigrationStatus.cs +++ b/backend/src/Squidex.Infrastructure.MongoDb/Migrations/MongoMigrationStatus.cs @@ -45,12 +45,18 @@ namespace Squidex.Infrastructure.Migrations return entity == null || entity.IsLocked == false; } - public Task UnlockAsync(int newVersion) + public Task CompleteAsync(int newVersion) { return Collection.UpdateOneAsync(x => x.Id == DefaultId, Update - .Set(x => x.IsLocked, false) .Set(x => x.Version, newVersion)); } + + public Task UnlockAsync() + { + return Collection.UpdateOneAsync(x => x.Id == DefaultId, + Update + .Set(x => x.IsLocked, false)); + } } } diff --git a/backend/src/Squidex.Infrastructure/Migrations/IMigrationStatus.cs b/backend/src/Squidex.Infrastructure/Migrations/IMigrationStatus.cs index 40c601118..23ad723ac 100644 --- a/backend/src/Squidex.Infrastructure/Migrations/IMigrationStatus.cs +++ b/backend/src/Squidex.Infrastructure/Migrations/IMigrationStatus.cs @@ -15,6 +15,8 @@ namespace Squidex.Infrastructure.Migrations Task TryLockAsync(); - Task UnlockAsync(int newVersion); + Task CompleteAsync(int newVersion); + + Task UnlockAsync(); } } diff --git a/backend/src/Squidex.Infrastructure/Migrations/Migrator.cs b/backend/src/Squidex.Infrastructure/Migrations/Migrator.cs index 6ded830f3..5aff98ff6 100644 --- a/backend/src/Squidex.Infrastructure/Migrations/Migrator.cs +++ b/backend/src/Squidex.Infrastructure/Migrations/Migrator.cs @@ -90,11 +90,13 @@ namespace Squidex.Infrastructure.Migrations } version = newVersion; + + await migrationStatus.CompleteAsync(newVersion); } } finally { - await migrationStatus.UnlockAsync(version); + await migrationStatus.UnlockAsync(); } } } diff --git a/backend/tests/Squidex.Infrastructure.Tests/Migrations/MigratorTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Migrations/MigratorTests.cs index 2888c3473..02f05b897 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Migrations/MigratorTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Migrations/MigratorTests.cs @@ -50,33 +50,81 @@ namespace Squidex.Infrastructure.Migrations return Task.FromResult(lockAcquired); } - public Task UnlockAsync(int newVersion) + public Task CompleteAsync(int newVersion) { lock (lockObject) { - isLocked = false; - version = newVersion; } return Task.CompletedTask; } + + public Task UnlockAsync() + { + lock (lockObject) + { + isLocked = false; + } + + return Task.CompletedTask; + } } public MigratorTests() { A.CallTo(() => path.GetNext(A._)) - .ReturnsLazily((int v) => + .ReturnsLazily((int version) => { - var m = migrations.Where(x => x.From == v).ToList(); + var selected = migrations.Where(x => x.From == version).ToList(); + + if (selected.Count == 0) + { + return (0, null); + } - return m.Count == 0 ? (0, null) : (migrations.Max(x => x.To), migrations.Select(x => x.Migration)); + var newVersion = selected.Max(x => x.To); + + return (newVersion, migrations.Select(x => x.Migration)); }); A.CallTo(() => status.GetVersionAsync()).Returns(0); A.CallTo(() => status.TryLockAsync()).Returns(true); } + [Fact] + public async Task Should_migrate_in_one_step() + { + var migrator_0_1 = BuildMigration(0, 1); + var migrator_1_2 = BuildMigration(0, 2); + var migrator_2_3 = BuildMigration(0, 3); + + var sut = new Migrator(status, path, log); + + await sut.MigrateAsync(); + + A.CallTo(() => migrator_0_1.UpdateAsync()) + .MustHaveHappened(); + + A.CallTo(() => migrator_1_2.UpdateAsync()) + .MustHaveHappened(); + + A.CallTo(() => migrator_2_3.UpdateAsync()) + .MustHaveHappened(); + + A.CallTo(() => status.CompleteAsync(1)) + .MustNotHaveHappened(); + + A.CallTo(() => status.CompleteAsync(2)) + .MustNotHaveHappened(); + + A.CallTo(() => status.CompleteAsync(3)) + .MustHaveHappened(); + + A.CallTo(() => status.UnlockAsync()) + .MustHaveHappened(); + } + [Fact] public async Task Should_migrate_step_by_step() { @@ -88,11 +136,25 @@ namespace Squidex.Infrastructure.Migrations await sut.MigrateAsync(); - A.CallTo(() => migrator_0_1.UpdateAsync()).MustHaveHappened(); - A.CallTo(() => migrator_1_2.UpdateAsync()).MustHaveHappened(); - A.CallTo(() => migrator_2_3.UpdateAsync()).MustHaveHappened(); + A.CallTo(() => migrator_0_1.UpdateAsync()) + .MustHaveHappened(); + + A.CallTo(() => migrator_1_2.UpdateAsync()) + .MustHaveHappened(); + + A.CallTo(() => migrator_2_3.UpdateAsync()) + .MustHaveHappened(); + + A.CallTo(() => status.CompleteAsync(1)) + .MustHaveHappened(); + + A.CallTo(() => status.CompleteAsync(2)) + .MustHaveHappened(); + + A.CallTo(() => status.CompleteAsync(3)) + .MustHaveHappened(); - A.CallTo(() => status.UnlockAsync(3)) + A.CallTo(() => status.UnlockAsync()) .MustHaveHappened(); } @@ -109,11 +171,26 @@ namespace Squidex.Infrastructure.Migrations await Assert.ThrowsAsync(() => sut.MigrateAsync()); - A.CallTo(() => migrator_0_1.UpdateAsync()).MustHaveHappened(); - A.CallTo(() => migrator_1_2.UpdateAsync()).MustHaveHappened(); - A.CallTo(() => migrator_2_3.UpdateAsync()).MustNotHaveHappened(); + A.CallTo(() => migrator_0_1.UpdateAsync()) + .MustHaveHappened(); - A.CallTo(() => status.UnlockAsync(0)).MustHaveHappened(); + A.CallTo(() => migrator_1_2.UpdateAsync()) + .MustHaveHappened(); + + A.CallTo(() => migrator_2_3.UpdateAsync()) + .MustNotHaveHappened(); + + A.CallTo(() => status.CompleteAsync(1)) + .MustNotHaveHappened(); + + A.CallTo(() => status.CompleteAsync(2)) + .MustNotHaveHappened(); + + A.CallTo(() => status.CompleteAsync(3)) + .MustNotHaveHappened(); + + A.CallTo(() => status.UnlockAsync()) + .MustHaveHappened(); } [Fact] @@ -142,7 +219,7 @@ namespace Squidex.Infrastructure.Migrations public async Task Should_prevent_multiple_updates() { var migrator_0_1 = BuildMigration(0, 1); - var migrator_1_2 = BuildMigration(1, 2); + var migrator_1_2 = BuildMigration(0, 2); var sut = new Migrator(new InMemoryStatus(), path, log) { LockWaitMs = 2 }; @@ -150,6 +227,7 @@ namespace Squidex.Infrastructure.Migrations A.CallTo(() => migrator_0_1.UpdateAsync()) .MustHaveHappenedOnceExactly(); + A.CallTo(() => migrator_1_2.UpdateAsync()) .MustHaveHappenedOnceExactly(); }