feat: implement strict raft consensus and convergence parity
This commit is contained in:
@@ -49,12 +49,24 @@ public sealed class RaftNode
|
||||
TryBecomeLeader(clusterSize);
|
||||
}
|
||||
|
||||
public VoteResponse GrantVote(int term)
|
||||
public VoteResponse GrantVote(int term, string candidateId = "")
|
||||
{
|
||||
if (term < TermState.CurrentTerm)
|
||||
return new VoteResponse { Granted = false };
|
||||
|
||||
TermState.CurrentTerm = term;
|
||||
if (term > TermState.CurrentTerm)
|
||||
{
|
||||
TermState.CurrentTerm = term;
|
||||
TermState.VotedFor = null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(TermState.VotedFor)
|
||||
&& !string.Equals(TermState.VotedFor, candidateId, StringComparison.Ordinal))
|
||||
{
|
||||
return new VoteResponse { Granted = false };
|
||||
}
|
||||
|
||||
TermState.VotedFor = candidateId;
|
||||
return new VoteResponse { Granted = true };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,41 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace NATS.Server.Raft;
|
||||
|
||||
public sealed class RaftSnapshotStore
|
||||
{
|
||||
private RaftSnapshot? _snapshot;
|
||||
private readonly string? _snapshotPath;
|
||||
|
||||
public RaftSnapshotStore(string? snapshotPath = null)
|
||||
{
|
||||
_snapshotPath = snapshotPath;
|
||||
}
|
||||
|
||||
public Task SaveAsync(RaftSnapshot snapshot, CancellationToken ct)
|
||||
{
|
||||
_snapshot = snapshot;
|
||||
if (!string.IsNullOrWhiteSpace(_snapshotPath))
|
||||
{
|
||||
var dir = Path.GetDirectoryName(_snapshotPath);
|
||||
if (!string.IsNullOrWhiteSpace(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
File.WriteAllText(_snapshotPath, JsonSerializer.Serialize(snapshot));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<RaftSnapshot?> LoadAsync(CancellationToken ct)
|
||||
{
|
||||
if (_snapshot == null
|
||||
&& !string.IsNullOrWhiteSpace(_snapshotPath)
|
||||
&& File.Exists(_snapshotPath))
|
||||
{
|
||||
_snapshot = JsonSerializer.Deserialize<RaftSnapshot>(File.ReadAllText(_snapshotPath));
|
||||
}
|
||||
|
||||
return Task.FromResult(_snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public sealed class InMemoryRaftTransport : IRaftTransport
|
||||
public Task<VoteResponse> RequestVoteAsync(string candidateId, string voterId, VoteRequest request, CancellationToken ct)
|
||||
{
|
||||
if (_nodes.TryGetValue(voterId, out var node))
|
||||
return Task.FromResult(node.GrantVote(request.Term));
|
||||
return Task.FromResult(node.GrantVote(request.Term, string.IsNullOrWhiteSpace(request.CandidateId) ? candidateId : request.CandidateId));
|
||||
|
||||
return Task.FromResult(new VoteResponse { Granted = false });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user