diff --git a/CHANGELOG.md b/CHANGELOG.md index 50dae742a..fe893b3ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Release Notes ==== +# 06-26-2024 +DotNext.Net.Cluster 5.7.1 +* Improved reliability of disk I/O for the new WAL binary format + +DotNext.AspNetCore.Cluster 5.7.1 +* Improved reliability of disk I/O for the new WAL binary format + # 06-25-2024 DotNext.Threading 5.8.0 * Introduced `WaitAnyAsync` method to wait on a group of cancellation tokens diff --git a/README.md b/README.md index 15a141b4e..ecc4146dc 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,13 @@ All these things are implemented in 100% managed code on top of existing .NET AP * [NuGet Packages](https://www.nuget.org/profiles/rvsakno) # What's new -Release Date: 06-25-2024 +Release Date: 06-26-2024 -DotNext.Threading 5.8.0 -* Introduced `WaitAnyAsync` method to wait on a group of cancellation tokens -* Added cancellation support for `WaitAsync` extension method for [WaitHandle](https://learn.microsoft.com/en-us/dotnet/api/system.threading.waithandle) class +DotNext.Net.Cluster 5.7.1 +* Improved reliability of disk I/O for the new WAL binary format + +DotNext.AspNetCore.Cluster 5.7.1 +* Improved reliability of disk I/O for the new WAL binary format Changelog for previous versions located [here](./CHANGELOG.md). diff --git a/src/DotNext.Tests/Threading/Leases/LeaseTests.cs b/src/DotNext.Tests/Threading/Leases/LeaseTests.cs index fc0ea51da..5adf162db 100644 --- a/src/DotNext.Tests/Threading/Leases/LeaseTests.cs +++ b/src/DotNext.Tests/Threading/Leases/LeaseTests.cs @@ -94,7 +94,7 @@ public static async Task ConsumerTokenState() False(consumer.Token.IsCancellationRequested); False(consumer.Expiration.IsExpired); - await Task.Delay(150); + await Task.Delay(400); True(consumer.Token.IsCancellationRequested); False(await consumer.ReleaseAsync()); } @@ -124,7 +124,7 @@ public static async Task AcquireUsingConsumer() [Fact] public static async Task WorkerProtectedWithLease() { - var pause = TimeSpan.FromMilliseconds(100); + var pause = TimeSpan.FromMilliseconds(500); using var provider = new TestLeaseProvider(pause); await using var consumer = new TestLeaseConsumer(provider); True(await consumer.TryAcquireAsync()); @@ -134,7 +134,7 @@ public static async Task WorkerProtectedWithLease() static async Task Worker(CancellationToken token) { - await Task.Delay(TimeSpan.FromMilliseconds(250), token); + await Task.Delay(TimeSpan.FromMilliseconds(1_000), token); return 42; } } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/DotNext.AspNetCore.Cluster.csproj b/src/cluster/DotNext.AspNetCore.Cluster/DotNext.AspNetCore.Cluster.csproj index a5c9a3b30..38fd71bbd 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/DotNext.AspNetCore.Cluster.csproj +++ b/src/cluster/DotNext.AspNetCore.Cluster/DotNext.AspNetCore.Cluster.csproj @@ -8,7 +8,7 @@ true true nullablePublicOnly - 5.7.0 + 5.7.1 .NET Foundation and Contributors .NEXT Family of Libraries diff --git a/src/cluster/DotNext.Net.Cluster/DotNext.Net.Cluster.csproj b/src/cluster/DotNext.Net.Cluster/DotNext.Net.Cluster.csproj index 2d2ec5bc7..3f10699ef 100644 --- a/src/cluster/DotNext.Net.Cluster/DotNext.Net.Cluster.csproj +++ b/src/cluster/DotNext.Net.Cluster/DotNext.Net.Cluster.csproj @@ -8,7 +8,7 @@ enable true nullablePublicOnly - 5.7.0 + 5.7.1 .NET Foundation and Contributors .NEXT Family of Libraries diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs index 9969baa9c..dfb072be8 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs @@ -15,12 +15,12 @@ public partial class PersistentState private protected abstract class Partition : ConcurrentStorageAccess { internal const int MaxRecordsPerPartition = int.MaxValue / LogEntryMetadata.Size; - protected static readonly CacheRecord EmptyRecord = new() { PersistenceMode = CachedLogEntryPersistenceMode.CopyToBuffer }; + private static readonly CacheRecord EmptyRecord = new() { PersistenceMode = CachedLogEntryPersistenceMode.CopyToBuffer }; internal readonly long FirstIndex, PartitionNumber, LastIndex; private Partition? previous, next; private object?[]? context; - protected MemoryOwner entryCache; + private MemoryOwner entryCache; protected int runningIndex; protected Partition(DirectoryInfo location, int offset, int bufferSize, int recordsPerPartition, long partitionNumber, in BufferManager manager, int readersCount, WriteMode writeMode, long initialSize, FileAttributes attributes = FileAttributes.NotContentIndexed) @@ -457,19 +457,6 @@ private sealed class Table : Partition, IReadOnlyList> { private const int HeaderSize = 512; - private static readonly ReadOnlyMemory EmptyMetadata; - private static readonly ReadOnlyMemory EphemeralMetadata; - - static Table() - { - EmptyMetadata = new byte[LogEntryMetadata.Size]; // all zeroes - - var ephemeral = LogEntryMetadata.Create(LogEntry.Initial, HeaderSize + LogEntryMetadata.Size, length: 0L); - var buffer = new byte[LogEntryMetadata.Size]; - ephemeral.Format(buffer); - EphemeralMetadata = buffer; - } - // metadata management private MemoryOwner header, footer; private (ReadOnlyMemory, ReadOnlyMemory) bufferTuple; @@ -485,7 +472,7 @@ internal Table(DirectoryInfo location, int bufferSize, int recordsPerPartition, // init ephemeral 0 entry if (PartitionNumber is 0L) { - EphemeralMetadata.CopyTo(footer.Memory); + LogEntryMetadata.Create(LogEntry.Initial, HeaderSize + LogEntryMetadata.Size, length: 0L).Format(footer.Span); } } @@ -517,6 +504,7 @@ internal override void Initialize() fileOffset -= footer.Length; RandomAccess.Read(Handle, footer.Span, fileOffset); + runningIndex = int.CreateChecked(LastIndex - FirstIndex); } else { @@ -534,7 +522,9 @@ internal override void Initialize() fileOffset = HeaderSize; } - for (Span metadataBuffer = stackalloc byte[LogEntryMetadata.Size], metadataTable = footer.Span; footerOffset < footer.Length; footerOffset += LogEntryMetadata.Size) + for (Span metadataBuffer = stackalloc byte[LogEntryMetadata.Size], metadataTable = footer.Span; + footerOffset < footer.Length; + footerOffset += LogEntryMetadata.Size, runningIndex++) { var count = RandomAccess.Read(Handle, metadataBuffer, fileOffset); if (count < LogEntryMetadata.Size) @@ -696,6 +686,7 @@ private async ValueTask FlushAndSealAsync(CancellationToken token) await WriteFooterAsync(token).ConfigureAwait(false); } + RandomAccess.FlushToDisk(Handle); RandomAccess.SetLength(Handle, writer.FilePosition + footer.Length); IsSealed = true;