diff --git a/src/SIL.Harmony.Tests/SnapshotTests.cs b/src/SIL.Harmony.Tests/SnapshotTests.cs index 617e213..8ab31b5 100644 --- a/src/SIL.Harmony.Tests/SnapshotTests.cs +++ b/src/SIL.Harmony.Tests/SnapshotTests.cs @@ -1,5 +1,8 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using SIL.Harmony.Changes; +using SIL.Harmony.Config; using SIL.Harmony.Sample.Changes; using SIL.Harmony.Sample.Models; @@ -172,4 +175,24 @@ public async Task RegenerateSnapshots_WillArriveAtTheSameState() //we probably won't have the same number of snapshots, which is ok. but none of the ids should be the same afterSnapshotsIds.Should().NotIntersectWith(beforeSnapshotIds); } + + [Fact] + public async Task RegenerateSnapshots_LeavesTheProjectIntactWhenTheRebuildFails() + { + var entityId = Guid.NewGuid(); + await WriteNextChange(SetWord(entityId, "test root")); + await WriteNextChange(SetWord(entityId, "test1")); + var beforeSnapshotIds = await DbContext.Snapshots.Select(s => s.Id).ToArrayAsync(TestContext.Current.CancellationToken); + var beforeRegenerate = await DataModel.QueryLatest().ToArrayAsync(TestContext.Current.CancellationToken); + + _services.GetRequiredService>().Value.BeforeSaveObject = + (_, _) => throw new InvalidOperationException("rebuild failed"); + Func act = () => DataModel.RegenerateSnapshots(); + await act.Should().ThrowAsync(); + + var afterSnapshotIds = await DbContext.Snapshots.Select(s => s.Id).ToArrayAsync(TestContext.Current.CancellationToken); + afterSnapshotIds.Should().BeEquivalentTo(beforeSnapshotIds); + var afterRegenerate = await DataModel.QueryLatest().ToArrayAsync(TestContext.Current.CancellationToken); + afterRegenerate.Should().BeEquivalentTo(beforeRegenerate); + } } diff --git a/src/SIL.Harmony/DataModel.cs b/src/SIL.Harmony/DataModel.cs index 01e5219..2b808aa 100644 --- a/src/SIL.Harmony/DataModel.cs +++ b/src/SIL.Harmony/DataModel.cs @@ -234,12 +234,15 @@ private async Task ValidateCommits(CrdtRepository repo) public async Task RegenerateSnapshots() { await using var repo = await _crdtRepositoryFactory.CreateRepository(); - await repo.DeleteSnapshotsAndProjectedTables(); + using var locked = await repo.Lock(); repo.ClearChangeTracker(); + await using var transaction = await repo.BeginTransactionAsync(); + await repo.DeleteSnapshotsAndProjectedTables(); var allCommits = await repo.CurrentCommits() .Include(c => c.ChangeEntities) .ToSortedSetAsync(); await UpdateSnapshots(repo, allCommits); + await transaction.CommitAsync(); } public async Task GetLatestSnapshotByObjectId(Guid entityId)