diff --git a/src/ZoneTree.UnitTests/BottomSegmentMergeTests.cs b/src/ZoneTree.UnitTests/BottomSegmentMergeTests.cs index 4e8625d..aa97fb1 100644 --- a/src/ZoneTree.UnitTests/BottomSegmentMergeTests.cs +++ b/src/ZoneTree.UnitTests/BottomSegmentMergeTests.cs @@ -270,7 +270,51 @@ public sealed class BottomSegmentMergeTests } } - static ZoneTreeOptions CreateMultiPartBottomMergeOptions() + [Test] + public void MultiPartCreatorDropBeforeHeaderIsWrittenDropsPendingPart() + { + var options = CreateMultiPartBottomMergeOptions( + "data/MultiPartCreatorDropBeforeHeaderIsWrittenDropsPendingPart"); + var idProvider = new IncrementalIdProvider(); + try + { + using var creator = new MultiPartDiskSegmentCreator( + options, + idProvider); + + creator.Append(1, 1, IteratorPosition.None); + + Assert.That( + options.RandomAccessDeviceManager.DeviceExists( + 2, + DiskSegmentConstants.DataCategory, + isCompressed: true), + Is.True); + + Assert.DoesNotThrow(() => creator.DropDiskSegment()); + + Assert.That( + options.RandomAccessDeviceManager.DeviceExists( + 1, + DiskSegmentConstants.MultiPartDiskSegmentCategory, + isCompressed: false), + Is.False); + Assert.That( + options.RandomAccessDeviceManager.DeviceExists( + 2, + DiskSegmentConstants.DataCategory, + isCompressed: true), + Is.False); + Assert.That(options.RandomAccessDeviceManager.DeviceCount, Is.EqualTo(0)); + } + finally + { + options.RandomAccessDeviceManager.DropStore(); + } + } + + static ZoneTreeOptions CreateMultiPartBottomMergeOptions( + string dataDirectory = "data/PartialMultiPartBottomMerge") { var logger = new ConsoleLogger(LogLevel.Error); var options = new ZoneTreeOptions @@ -283,7 +327,7 @@ public sealed class BottomSegmentMergeTests RandomAccessDeviceManager = new RandomAccessDeviceManager( logger, new InMemoryFileStreamProvider(), - "data/PartialMultiPartBottomMerge"), + dataDirectory), DiskSegmentOptions = new DiskSegmentOptions { DiskSegmentMode = DiskSegmentMode.MultiPartDiskSegment, diff --git a/src/ZoneTree/Segments/MultiPart/MultiPartDiskSegmentCreator.cs b/src/ZoneTree/Segments/MultiPart/MultiPartDiskSegmentCreator.cs index 5d0347b..66a7a6b 100644 --- a/src/ZoneTree/Segments/MultiPart/MultiPartDiskSegmentCreator.cs +++ b/src/ZoneTree/Segments/MultiPart/MultiPartDiskSegmentCreator.cs @@ -133,6 +133,7 @@ public sealed class MultiPartDiskSegmentCreator : IDiskSegmentCrea if (NextCreator.Length == 0) { NextCreator.DropDiskSegment(); + NextCreator = null; } else { @@ -140,6 +141,7 @@ public sealed class MultiPartDiskSegmentCreator : IDiskSegmentCrea PartValues.Add(LastAppendedValue); var part = NextCreator.CreateReadOnlyDiskSegment(); Parts.Add(part); + NextCreator = null; } WriteMultiDiskSegment(); @@ -238,21 +240,34 @@ public sealed class MultiPartDiskSegmentCreator : IDiskSegmentCrea continue; part.Drop(); } - using var multiDevice = Options.RandomAccessDeviceManager - .GetReadOnlyDevice( - SegmentId, - DiskSegmentConstants.MultiPartDiskSegmentCategory, - isCompressed: false, - compressionBlockSize: 0, - MultiPartDiskSegment.MultiPartHeaderCompressionMethod, - MultiPartDiskSegment.MultiPartHeaderCompressionLevel); + + NextCreator?.DropDiskSegment(); + NextCreator = null; + + var randomAccessDeviceManager = Options.RandomAccessDeviceManager; + if (!randomAccessDeviceManager.DeviceExists( + SegmentId, + DiskSegmentConstants.MultiPartDiskSegmentCategory, + isCompressed: false)) + { + return; + } + + using var multiDevice = randomAccessDeviceManager.GetReadOnlyDevice( + SegmentId, + DiskSegmentConstants.MultiPartDiskSegmentCategory, + isCompressed: false, + compressionBlockSize: 0, + MultiPartDiskSegment.MultiPartHeaderCompressionMethod, + MultiPartDiskSegment.MultiPartHeaderCompressionLevel); multiDevice.Delete(); - Options.RandomAccessDeviceManager - .RemoveReadOnlyDevice(SegmentId, DiskSegmentConstants.MultiPartDiskSegmentCategory); + randomAccessDeviceManager.RemoveReadOnlyDevice( + SegmentId, + DiskSegmentConstants.MultiPartDiskSegmentCategory); } public void Dispose() { } -} \ No newline at end of file +}