feat: add quorum check before proposing entries (Gap 8.6)
Add HasQuorum() to RaftNode that counts peers with LastContact within 2 × ElectionTimeoutMaxMs and returns true only when self + current peers reaches majority. ProposeAsync now throws InvalidOperationException with "no quorum" when HasQuorum() returns false, preventing a partitioned leader from diverging the log. Add 14 tests in RaftQuorumCheckTests.cs covering single-node, 3-node, 5-node, boundary window, and heartbeat restore scenarios. Update RaftHealthTests.LastContact_updates_on_successful_replication to avoid triggering the new quorum guard.
This commit is contained in:
@@ -301,15 +301,15 @@ public class RaftHealthTests
|
||||
var (nodes, _) = CreateCluster(3);
|
||||
var leader = ElectLeader(nodes);
|
||||
|
||||
// Set peer contacts in the past
|
||||
foreach (var (_, state) in leader.GetPeerStates())
|
||||
state.LastContact = DateTime.UtcNow.AddMinutes(-5);
|
||||
// Record timestamps just before proposing (peers are fresh from ConfigureCluster).
|
||||
var beforePropose = DateTime.UtcNow;
|
||||
|
||||
await leader.ProposeAsync("cmd-1", default);
|
||||
|
||||
// Successful replication should update LastContact
|
||||
// Successful replication should update LastContact to at least the time we
|
||||
// recorded before the propose call.
|
||||
foreach (var (_, state) in leader.GetPeerStates())
|
||||
state.LastContact.ShouldBeGreaterThan(DateTime.UtcNow.AddSeconds(-2));
|
||||
state.LastContact.ShouldBeGreaterThanOrEqualTo(beforePropose);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user