using ZB.MOM.WW.CBDD.Bson; using ZB.MOM.WW.CBDD.Core.Storage; using ZB.MOM.WW.CBDD.Shared; namespace ZB.MOM.WW.CBDD.Tests; public class CompactionWalCoordinationTests { [Fact] public void OfflineCompact_ShouldCheckpointAndLeaveWalEmpty() { var dbPath = NewDbPath(); var markerPath = $"{dbPath}.compact.state"; try { using var db = new TestDbContext(dbPath); for (var i = 0; i < 80; i++) { db.Users.Insert(new User { Name = $"wal-compact-{i:D3}", Age = i }); } db.SaveChanges(); db.Storage.GetWalSize().ShouldBeGreaterThan(0); var stats = db.Compact(new CompactionOptions { EnableTailTruncation = true, NormalizeFreeList = true, DefragmentSlottedPages = true }); stats.OnlineMode.ShouldBeFalse(); db.Storage.GetWalSize().ShouldBe(0); File.Exists(markerPath).ShouldBeFalse(); db.Users.Count().ShouldBe(80); } finally { CleanupFiles(dbPath); } } [Fact] public void Compact_AfterWalRecovery_ShouldKeepDataDurable() { var dbPath = NewDbPath(); var walPath = Path.ChangeExtension(dbPath, ".wal"); var expected = new List<(ObjectId Id, string Name)>(); try { using (var writer = new TestDbContext(dbPath)) { for (var i = 0; i < 48; i++) { var name = $"recoverable-{i:D3}"; var id = writer.Users.Insert(new User { Name = name, Age = i % 13 }); expected.Add((id, name)); } writer.SaveChanges(); writer.Storage.GetWalSize().ShouldBeGreaterThan(0); } new FileInfo(walPath).Length.ShouldBeGreaterThan(0); using (var recovered = new TestDbContext(dbPath)) { recovered.Users.Count().ShouldBe(expected.Count); foreach (var item in expected) { recovered.Users.FindById(item.Id)!.Name.ShouldBe(item.Name); } recovered.SaveChanges(); recovered.Compact(); recovered.Storage.GetWalSize().ShouldBe(0); } using (var verify = new TestDbContext(dbPath)) { verify.Users.Count().ShouldBe(expected.Count); foreach (var item in expected) { verify.Users.FindById(item.Id)!.Name.ShouldBe(item.Name); } } } finally { CleanupFiles(dbPath); } } private static string NewDbPath() => Path.Combine(Path.GetTempPath(), $"compaction_wal_{Guid.NewGuid():N}.db"); private static void CleanupFiles(string dbPath) { var walPath = Path.ChangeExtension(dbPath, ".wal"); var markerPath = $"{dbPath}.compact.state"; if (File.Exists(dbPath)) File.Delete(dbPath); if (File.Exists(walPath)) File.Delete(walPath); if (File.Exists(markerPath)) File.Delete(markerPath); } }