feat: implement strict raft consensus and convergence parity
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
using NATS.Server.Raft;
|
||||
|
||||
namespace NATS.Server.Tests.Raft;
|
||||
|
||||
public class RaftStrictConsensusRuntimeTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Quorum_and_nextindex_rules_gate_commit_visibility_and_snapshot_catchup_convergence()
|
||||
{
|
||||
var voter = new RaftNode("v1");
|
||||
voter.GrantVote(2, "cand-a").Granted.ShouldBeTrue();
|
||||
voter.GrantVote(2, "cand-b").Granted.ShouldBeFalse();
|
||||
|
||||
var transport = new RejectingRaftTransport();
|
||||
var leader = new RaftNode("n1", transport);
|
||||
var followerA = new RaftNode("n2", transport);
|
||||
var followerB = new RaftNode("n3", transport);
|
||||
var cluster = new[] { leader, followerA, followerB };
|
||||
foreach (var node in cluster)
|
||||
node.ConfigureCluster(cluster);
|
||||
|
||||
leader.StartElection(cluster.Length);
|
||||
leader.ReceiveVote(new VoteResponse { Granted = true }, cluster.Length);
|
||||
leader.IsLeader.ShouldBeTrue();
|
||||
|
||||
_ = await leader.ProposeAsync("cmd-1", default);
|
||||
leader.AppliedIndex.ShouldBe(0);
|
||||
followerA.AppliedIndex.ShouldBe(0);
|
||||
followerB.AppliedIndex.ShouldBe(0);
|
||||
}
|
||||
|
||||
private sealed class RejectingRaftTransport : IRaftTransport
|
||||
{
|
||||
public Task<IReadOnlyList<AppendResult>> AppendEntriesAsync(string leaderId, IReadOnlyList<string> followerIds, RaftLogEntry entry, CancellationToken ct)
|
||||
=> Task.FromResult<IReadOnlyList<AppendResult>>(
|
||||
followerIds.Select(id => new AppendResult { FollowerId = id, Success = false }).ToArray());
|
||||
|
||||
public Task<VoteResponse> RequestVoteAsync(string candidateId, string voterId, VoteRequest request, CancellationToken ct)
|
||||
=> Task.FromResult(new VoteResponse { Granted = true });
|
||||
|
||||
public Task InstallSnapshotAsync(string leaderId, string followerId, RaftSnapshot snapshot, CancellationToken ct)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using NATS.Server.Raft;
|
||||
|
||||
namespace NATS.Server.Tests.Raft;
|
||||
|
||||
public class RaftStrictConvergenceRuntimeTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Quorum_and_nextindex_rules_gate_commit_visibility_and_snapshot_catchup_convergence()
|
||||
{
|
||||
var file = Path.Combine(Path.GetTempPath(), $"nats-raft-snapshot-{Guid.NewGuid():N}.json");
|
||||
|
||||
try
|
||||
{
|
||||
var first = new RaftSnapshotStore(file);
|
||||
await first.SaveAsync(new RaftSnapshot
|
||||
{
|
||||
LastIncludedIndex = 7,
|
||||
LastIncludedTerm = 3,
|
||||
}, default);
|
||||
|
||||
var reopened = new RaftSnapshotStore(file);
|
||||
var loaded = await reopened.LoadAsync(default);
|
||||
loaded.ShouldNotBeNull();
|
||||
loaded.LastIncludedIndex.ShouldBe(7);
|
||||
loaded.LastIncludedTerm.ShouldBe(3);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(file))
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user