Skip to content

Commit

Permalink
Release 5.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
sakno committed Feb 25, 2024
1 parent fba52c8 commit a0427a7
Show file tree
Hide file tree
Showing 18 changed files with 206 additions and 166 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
Release Notes
====

# 02-25-2024
<a href="https://www.nuget.org/packages/dotnext/5.0.3">DotNext 5.0.3</a>
* Fixed behavior to no-op when `GCLatencyModeScope` is initialized to default

<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.0.3">DotNext.Metaprogramming 5.0.3</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.unsafe/5.0.3">DotNext.Unsafe 5.0.3</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.threading/5.0.3">DotNext.Threading 5.0.3</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.io/5.0.3">DotNext.IO 5.0.3</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.0.3">DotNext.Net.Cluster 5.0.3</a>
* Attempt to fix [221](https://github.com/dotnet/dotNext/issues/221)

<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.0.3">DotNext.AspNetCore.Cluster 5.0.3</a>
* Attempt to fix [221](https://github.com/dotnet/dotNext/issues/221)

# 02-17-2024
<a href="https://www.nuget.org/packages/dotnext/5.0.2">DotNext 5.0.2</a>
* Fixed XML docs
Expand Down
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,28 @@ 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: 02-17-2024
Release Date: 02-25-2024

<a href="https://www.nuget.org/packages/dotnext/5.0.2">DotNext 5.0.2</a>
* Fixed XML docs
<a href="https://www.nuget.org/packages/dotnext/5.0.3">DotNext 5.0.3</a>
* Fixed behavior to no-op when `GCLatencyModeScope` is initialized to default

<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.0.2">DotNext.Metaprogramming 5.0.2</a>
* Fixed [223](https://github.com/dotnet/dotNext/issues/223)
<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.0.3">DotNext.Metaprogramming 5.0.3</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.unsafe/5.0.2">DotNext.Unsafe 5.0.2</a>
<a href="https://www.nuget.org/packages/dotnext.unsafe/5.0.3">DotNext.Unsafe 5.0.3</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.threading/5.0.2">DotNext.Threading 5.0.2</a>
* Added correct validation for maximum possible timeout for all `WaitAsync` methods
<a href="https://www.nuget.org/packages/dotnext.threading/5.0.3">DotNext.Threading 5.0.3</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.io/5.0.2">DotNext.IO 5.0.2</a>
<a href="https://www.nuget.org/packages/dotnext.io/5.0.3">DotNext.IO 5.0.3</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.0.2">DotNext.Net.Cluster 5.0.2</a>
* Prevent indexing of WAL files on Windows
<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.0.3">DotNext.Net.Cluster 5.0.3</a>
* Attempt to fix [221](https://github.com/dotnet/dotNext/issues/221)

<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.0.2">DotNext.AspNetCore.Cluster 5.0.2</a>
* Updated dependencies
<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.0.3">DotNext.AspNetCore.Cluster 5.0.3</a>
* Attempt to fix [221](https://github.com/dotnet/dotNext/issues/221)

Changelog for previous versions located [here](./CHANGELOG.md).

Expand Down
2 changes: 1 addition & 1 deletion src/DotNext.IO/DotNext.IO.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<Authors>.NET Foundation and Contributors</Authors>
<Company />
<Product>.NEXT Family of Libraries</Product>
<VersionPrefix>5.0.2</VersionPrefix>
<VersionPrefix>5.0.3</VersionPrefix>
<VersionSuffix></VersionSuffix>
<AssemblyName>DotNext.IO</AssemblyName>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand Down
2 changes: 1 addition & 1 deletion src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<ImplicitUsings>true</ImplicitUsings>
<IsTrimmable>false</IsTrimmable>
<Features>nullablePublicOnly</Features>
<VersionPrefix>5.0.2</VersionPrefix>
<VersionPrefix>5.0.3</VersionPrefix>
<VersionSuffix></VersionSuffix>
<Authors>.NET Foundation</Authors>
<Product>.NEXT Family of Libraries</Product>
Expand Down
2 changes: 1 addition & 1 deletion src/DotNext.Threading/DotNext.Threading.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ImplicitUsings>true</ImplicitUsings>
<IsTrimmable>true</IsTrimmable>
<Features>nullablePublicOnly</Features>
<VersionPrefix>5.0.2</VersionPrefix>
<VersionPrefix>5.0.3</VersionPrefix>
<VersionSuffix></VersionSuffix>
<Authors>.NET Foundation and Contributors</Authors>
<Product>.NEXT Family of Libraries</Product>
Expand Down
2 changes: 1 addition & 1 deletion src/DotNext.Unsafe/DotNext.Unsafe.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<IsTrimmable>true</IsTrimmable>
<VersionPrefix>5.0.2</VersionPrefix>
<VersionPrefix>5.0.3</VersionPrefix>
<Features>nullablePublicOnly</Features>
<VersionSuffix></VersionSuffix>
<Authors>.NET Foundation and Contributors</Authors>
Expand Down
2 changes: 1 addition & 1 deletion src/DotNext/DotNext.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<Authors>.NET Foundation and Contributors</Authors>
<Company />
<Product>.NEXT Family of Libraries</Product>
<VersionPrefix>5.0.2</VersionPrefix>
<VersionPrefix>5.0.3</VersionPrefix>
<VersionSuffix></VersionSuffix>
<AssemblyName>DotNext</AssemblyName>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand Down
8 changes: 6 additions & 2 deletions src/DotNext/Runtime/GCLatencyModeScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace DotNext.Runtime;
[StructLayout(LayoutKind.Auto)]
public readonly struct GCLatencyModeScope : IDisposable
{
private readonly GCLatencyMode currentMode;
private readonly GCLatencyMode? currentMode;

/// <summary>
/// Initializes a new scope that affects GC intrusion level.
Expand All @@ -24,7 +24,11 @@ public GCLatencyModeScope(GCLatencyMode mode)
/// <summary>
/// Cancels previously defined GC latency.
/// </summary>
public void Dispose() => GCSettings.LatencyMode = currentMode;
public void Dispose()
{
if (currentMode.HasValue)
GCSettings.LatencyMode = currentMode.GetValueOrDefault();
}

/// <summary>
/// Creates a scope with <see cref="GCLatencyMode.SustainedLowLatency"/> GC intrusion level.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<ImplicitUsings>true</ImplicitUsings>
<IsTrimmable>true</IsTrimmable>
<Features>nullablePublicOnly</Features>
<VersionPrefix>5.0.2</VersionPrefix>
<VersionPrefix>5.0.3</VersionPrefix>
<VersionSuffix></VersionSuffix>
<Authors>.NET Foundation and Contributors</Authors>
<Product>.NEXT Family of Libraries</Product>
Expand Down
2 changes: 1 addition & 1 deletion src/cluster/DotNext.Net.Cluster/DotNext.Net.Cluster.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<Nullable>enable</Nullable>
<IsTrimmable>true</IsTrimmable>
<Features>nullablePublicOnly</Features>
<VersionPrefix>5.0.2</VersionPrefix>
<VersionPrefix>5.0.3</VersionPrefix>
<VersionSuffix></VersionSuffix>
<Authors>.NET Foundation and Contributors</Authors>
<Product>.NEXT Family of Libraries</Product>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,114 +7,135 @@ namespace DotNext.Net.Cluster.Consensus.Raft;
using Runtime.CompilerServices;
using Threading.Tasks;

internal sealed class CandidateState<TMember>(IRaftStateMachine<TMember> stateMachine, long term) : RaftState<TMember>(stateMachine)
internal sealed class CandidateState<TMember> : RaftState<TMember>
where TMember : class, IRaftClusterMember
{
private enum VotingResult : byte
private readonly CancellationTokenSource votingCancellation;
private readonly CancellationToken votingCancellationToken; // cached to prevent ObjectDisposedException
internal readonly long Term;
private Task? votingTask;

public CandidateState(IRaftStateMachine<TMember> stateMachine, long term)
: base(stateMachine)
{
Rejected = 0,
Granted,
Canceled,
NotAvailable,
Term = term;
votingCancellation = new();
votingCancellationToken = votingCancellation.Token;
}

private readonly CancellationTokenSource votingCancellation = new();
internal readonly long Term = term;
private Task? votingTask;

[AsyncMethodBuilder(typeof(SpawningAsyncTaskMethodBuilder))]
private async Task VoteAsync(TimeSpan timeout, IAuditTrail<IRaftLogEntry> auditTrail)
{
// Perf: reuse index and related term once for all members
var lastIndex = auditTrail.LastEntryIndex;
var lastTerm = await auditTrail.GetTermAsync(lastIndex, votingCancellation.Token).ConfigureAwait(false);
var lastTerm = await auditTrail.GetTermAsync(lastIndex, votingCancellationToken).ConfigureAwait(false);

// start voting in parallel
var voters = StartVoting(Members, Term, lastIndex, lastTerm, votingCancellation.Token);
var voters = StartVoting(Members, Term, lastIndex, lastTerm, votingCancellationToken);
votingCancellation.CancelAfter(timeout);

// finish voting
await EndVoting(voters.GetConsumer(), votingCancellation.Token).ConfigureAwait(false);

static TaskCompletionPipe<Task<(TMember, long, VotingResult)>> StartVoting(IReadOnlyCollection<TMember> members, long currentTerm, long lastIndex, long lastTerm, CancellationToken token)
static TaskCompletionPipe<Task<(TMember, long, bool?)>> StartVoting(IReadOnlyCollection<TMember> members, long currentTerm, long lastIndex, long lastTerm, CancellationToken token)
{
var voters = new TaskCompletionPipe<Task<(TMember, long, VotingResult)>>();
var voters = new TaskCompletionPipe<Task<(TMember, long, bool?)>>();

// start voting in parallel
foreach (var member in members)
voters.Add(VoteAsync(member, currentTerm, lastIndex, lastTerm, token));
using (var enumerator = members.GetEnumerator())
{
while (enumerator.MoveNext() && !token.IsCancellationRequested)
{
voters.Add(VoteAsync(enumerator.Current, currentTerm, lastIndex, lastTerm, token));
}
}

voters.Complete();
return voters;
}

[AsyncMethodBuilder(typeof(SpawningAsyncTaskMethodBuilder<>))]
static async Task<(TMember, long, VotingResult)> VoteAsync(TMember voter, long currentTerm, long lastIndex, long lastTerm, CancellationToken token)
static async Task<(TMember, long, bool?)> VoteAsync(TMember voter, long currentTerm, long lastIndex, long lastTerm, CancellationToken token)
{
VotingResult result;
bool? result;
try
{
var response = await voter.VoteAsync(currentTerm, lastIndex, lastTerm, token).ConfigureAwait(false);
currentTerm = response.Term;
result = response.Value ? VotingResult.Granted : VotingResult.Rejected;
}
catch (OperationCanceledException)
{
result = VotingResult.Canceled;
result = response.Value;
}
catch (MemberUnavailableException)
{
result = VotingResult.NotAvailable;
result = null;
currentTerm = -1L;
}

return (voter, currentTerm, result);
}
}

private async Task EndVoting(IAsyncEnumerable<(TMember, long, VotingResult)> voters, CancellationToken token)
private async Task EndVoting(TaskCompletionPipe.Consumer<(TMember, long, bool?)> voters, CancellationToken token)
{
var votes = 0;
var localMember = default(TMember);
await foreach (var (member, term, result) in voters.ConfigureAwait(false))
{
if (IsDisposingOrDisposed)
return;

// current node is outdated
if (term > Term)
var enumerator = voters.GetAsyncEnumerator(token);
try
{
while (await enumerator.MoveNextAsync().ConfigureAwait(false))
{
MoveToFollowerState(randomizeTimeout: false, term);
return;
}
var (member, term, result) = enumerator.Current;

switch (result)
{
case VotingResult.Canceled: // candidate timeout happened
MoveToFollowerState(randomizeTimeout: false);
if (IsDisposingOrDisposed)
return;
case VotingResult.Granted:
Logger.VoteGranted(member.EndPoint);
votes += 1;
break;
case VotingResult.Rejected:
Logger.VoteRejected(member.EndPoint);
votes -= 1;
break;
case VotingResult.NotAvailable:
Logger.MemberUnavailable(member.EndPoint);
votes -= 1;
break;
}

if (!member.IsRemote)
localMember = member;
// current node is outdated
if (term > Term)
{
MoveToFollowerState(randomizeTimeout: false, term);
return;
}

switch (result)
{
case true:
Logger.VoteGranted(member.EndPoint);
votes += 1;
break;
case false:
Logger.VoteRejected(member.EndPoint);
votes -= 1;
break;
default:
Logger.MemberUnavailable(member.EndPoint);
votes -= 1;
break;
}

if (!member.IsRemote)
localMember = member;
}
}
catch (OperationCanceledException)
{
// candidate timeout happened
MoveToFollowerState(randomizeTimeout: false);
return;
}
finally
{
await enumerator.DisposeAsync().ConfigureAwait(false);
}

Logger.VotingCompleted(votes, Term);
if (token.IsCancellationRequested || votes <= 0 || localMember is null)
{
MoveToFollowerState(randomizeTimeout: true); // no clear consensus
}
else
{
MoveToLeaderState(localMember); // becomes a leader
}
}

/// <summary>
Expand All @@ -133,7 +154,7 @@ protected override async ValueTask DisposeAsyncCore()
{
try
{
votingCancellation.Cancel();
votingCancellation.Cancel(throwOnFirstException: false);
await (votingTask ?? Task.CompletedTask).ConfigureAwait(false);
}
catch (Exception e)
Expand All @@ -151,6 +172,7 @@ protected override void Dispose(bool disposing)
if (disposing)
{
votingCancellation.Dispose();
votingTask = null;
}

base.Dispose(disposing);
Expand Down
Loading

0 comments on commit a0427a7

Please sign in to comment.