feat(raft): add membership proposals, snapshot checkpoints, and log compaction (B4+B5+B6)
- ProposeAddPeerAsync/ProposeRemovePeerAsync: single-change-at-a-time membership changes through RAFT consensus (Go ref: raft.go:961-1019) - RaftLog.Compact: removes entries up to given index for log compaction - CreateSnapshotCheckpointAsync: creates snapshot and compacts log in one operation - DrainAndReplaySnapshotAsync: drains commit queue, installs snapshot, resets indices - Pre-vote protocol skipped (Go NATS doesn't implement it either) - 23 new tests in RaftMembershipAndSnapshotTests
This commit is contained in:
49
src/NATS.Server/Raft/RaftMembership.cs
Normal file
49
src/NATS.Server/Raft/RaftMembership.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
namespace NATS.Server.Raft;
|
||||
|
||||
/// <summary>
|
||||
/// Type of membership change operation.
|
||||
/// Go reference: raft.go:2500-2600 (ProposeAddPeer/RemovePeer)
|
||||
/// </summary>
|
||||
public enum RaftMembershipChangeType
|
||||
{
|
||||
AddPeer,
|
||||
RemovePeer,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a pending RAFT membership change (add or remove peer).
|
||||
/// Serialized as "{Type}:{PeerId}" in log entry commands for wire compatibility.
|
||||
/// Go reference: raft.go:2500-2600 (membership change proposals)
|
||||
/// </summary>
|
||||
public readonly record struct RaftMembershipChange(RaftMembershipChangeType Type, string PeerId)
|
||||
{
|
||||
/// <summary>
|
||||
/// Encodes this membership change as a log entry command string.
|
||||
/// Format: "AddPeer:node-id" or "RemovePeer:node-id"
|
||||
/// </summary>
|
||||
public string ToCommand() => $"{Type}:{PeerId}";
|
||||
|
||||
/// <summary>
|
||||
/// Parses a log entry command string back into a membership change.
|
||||
/// Returns null if the command is not a membership change.
|
||||
/// </summary>
|
||||
public static RaftMembershipChange? TryParse(string command)
|
||||
{
|
||||
var colonIndex = command.IndexOf(':');
|
||||
if (colonIndex < 0)
|
||||
return null;
|
||||
|
||||
var typePart = command[..colonIndex];
|
||||
var peerPart = command[(colonIndex + 1)..];
|
||||
|
||||
if (string.IsNullOrEmpty(peerPart))
|
||||
return null;
|
||||
|
||||
return typePart switch
|
||||
{
|
||||
nameof(RaftMembershipChangeType.AddPeer) => new RaftMembershipChange(RaftMembershipChangeType.AddPeer, peerPart),
|
||||
nameof(RaftMembershipChangeType.RemovePeer) => new RaftMembershipChange(RaftMembershipChangeType.RemovePeer, peerPart),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user