feat(raft): add commit queue, election timers, and peer health tracking (B1+B2+B3)

- CommitQueue<T>: channel-based queue for committed entries awaiting state machine application
- RaftPeerState: tracks replication and health state (nextIndex, matchIndex, lastContact)
- RaftNode: CommitIndex/ProcessedIndex tracking, election timer with randomized 150-300ms interval,
  peer state integration with heartbeat and replication updates
- 52 new tests across RaftApplyQueueTests, RaftElectionTimerTests, RaftHealthTests
This commit is contained in:
Joseph Doherty
2026-02-24 17:01:00 -05:00
parent 579063dabd
commit 5b706c969d
6 changed files with 1125 additions and 2 deletions

View File

@@ -0,0 +1,46 @@
namespace NATS.Server.Raft;
/// <summary>
/// Tracks replication and health state for a single RAFT peer.
/// Go reference: raft.go peer tracking fields (nextIndex, matchIndex, last contact).
/// </summary>
public sealed class RaftPeerState
{
/// <summary>
/// The peer's unique node identifier.
/// </summary>
public required string PeerId { get; init; }
/// <summary>
/// The next log index to send to this peer (leader use only).
/// </summary>
public long NextIndex { get; set; } = 1;
/// <summary>
/// The highest log index known to be replicated on this peer.
/// </summary>
public long MatchIndex { get; set; }
/// <summary>
/// Timestamp of the last successful communication with this peer.
/// </summary>
public DateTime LastContact { get; set; } = DateTime.UtcNow;
/// <summary>
/// Whether this peer is considered active in the cluster.
/// </summary>
public bool Active { get; set; } = true;
/// <summary>
/// Returns true if this peer has been contacted within the election timeout window.
/// Go reference: raft.go isCurrent check.
/// </summary>
public bool IsCurrent(TimeSpan electionTimeout)
=> DateTime.UtcNow - LastContact < electionTimeout;
/// <summary>
/// Returns true if this peer is both active and has been contacted within the health threshold.
/// </summary>
public bool IsHealthy(TimeSpan healthThreshold)
=> Active && DateTime.UtcNow - LastContact < healthThreshold;
}