From 041d4e22a38ca27dd08900906c69f62879bcc700 Mon Sep 17 00:00:00 2001 From: Ahmed Yasin Koculu Date: Mon, 8 Jun 2026 01:47:19 +0200 Subject: [PATCH] Fix first multipart merge disk segment detection --- .../FixedSizeKeyAndValueTests.cs | 34 ++++++++++++++++++- src/ZoneTree/Core/ZoneTree.Merge.cs | 14 +++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/ZoneTree.UnitTests/FixedSizeKeyAndValueTests.cs b/src/ZoneTree.UnitTests/FixedSizeKeyAndValueTests.cs index ad3bf35..2d7115b 100644 --- a/src/ZoneTree.UnitTests/FixedSizeKeyAndValueTests.cs +++ b/src/ZoneTree.UnitTests/FixedSizeKeyAndValueTests.cs @@ -34,6 +34,38 @@ public sealed class FixedSizeKeyAndValueTests data.Maintenance.Drop(); } + [Test] + public void FirstMultiPartMergeDoesNotTreatReadOnlySegmentAsDisk() + { + var dataPath = "data/FirstMultiPartMergeDoesNotTreatReadOnlySegmentAsDisk"; + if (Directory.Exists(dataPath)) + Directory.Delete(dataPath, true); + + using var data = new ZoneTreeFactory() + .DisableDeletion() + .SetMutableSegmentMaxItemCount(100) + .SetDataDirectory(dataPath) + .SetWriteAheadLogDirectory(dataPath) + .ConfigureDiskSegmentOptions(options => + { + options.DiskSegmentMode = DiskSegmentMode.MultiPartDiskSegment; + options.MinimumRecordCount = 2; + options.MaximumRecordCount = 3; + }) + .OpenOrCreate(); + + for (var i = 0; i < 4; ++i) + { + data.Upsert(i, i); + } + + data.Maintenance.MoveMutableSegmentForward(); + data.Maintenance.StartMergeOperation().Join(); + + Assert.That(data.Maintenance.DiskSegment.GetPartCount(), Is.EqualTo(2)); + data.Maintenance.Drop(); + } + [Test] public void IntStringTreeTest() { @@ -357,4 +389,4 @@ public sealed class FixedSizeKeyAndValueTests ReloadIntIntTreeTestHelper(dataPath, true); } -} \ No newline at end of file +} diff --git a/src/ZoneTree/Core/ZoneTree.Merge.cs b/src/ZoneTree/Core/ZoneTree.Merge.cs index 06ef651..740283e 100644 --- a/src/ZoneTree/Core/ZoneTree.Merge.cs +++ b/src/ZoneTree/Core/ZoneTree.Merge.cs @@ -138,7 +138,8 @@ public sealed partial class ZoneTree : IZoneTree, IZ var mergingSegments = new List>(); mergingSegments.AddRange(readOnlySegmentsArray); - if (oldDiskSegment is not NullDiskSegment) + var hasDiskSegment = oldDiskSegment is not NullDiskSegment; + if (hasDiskSegment) mergingSegments.Add(oldDiskSegment.GetSeekableIterator()); if (IsCancelMergeRequested) @@ -154,7 +155,7 @@ public sealed partial class ZoneTree : IZoneTree, IZ Options.DiskSegmentOptions.DiskSegmentMode == DiskSegmentMode.MultiPartDiskSegment; var len = mergingSegments.Count; - var diskSegmentIndex = len - 1; + var diskSegmentIndex = hasDiskSegment ? len - 1 : -1; using IDiskSegmentCreator diskSegmentCreator = enableMultiPartDiskSegment ? @@ -199,9 +200,12 @@ public sealed partial class ZoneTree : IZoneTree, IZ var hasPrev = false; TKey prevKey = default; - var firstKeysOfEveryPart = oldDiskSegment.GetFirstKeysOfEveryPart(); - var lastKeysOfEveryPart = oldDiskSegment.GetLastKeysOfEveryPart(); - var lastValuesOfEveryPart = oldDiskSegment.GetLastValuesOfEveryPart(); + var lastKeysOfEveryPart = hasDiskSegment ? + oldDiskSegment.GetLastKeysOfEveryPart() : + Array.Empty(); + var lastValuesOfEveryPart = hasDiskSegment ? + oldDiskSegment.GetLastValuesOfEveryPart() : + Array.Empty(); var diskSegmentMinimumRecordCount = Options.DiskSegmentOptions.MinimumRecordCount; var dropCount = 0;