fix(raft): ignore intermediate InstallSnapshot chunk replies
This commit is contained in:
@@ -679,7 +679,13 @@ proc handleInstallSnapshotReply*(node: RaftNode, peerId: string,
|
|||||||
if node.state != rsLeader:
|
if node.state != rsLeader:
|
||||||
return
|
return
|
||||||
|
|
||||||
if reply.success:
|
if reply.success and reply.matchIdx >= node.lastSnapshotIndex:
|
||||||
|
# The follower has actually adopted the snapshot base. Intermediate chunk
|
||||||
|
# replies (the T8 follower acks every non-done chunk with success=true and
|
||||||
|
# matchIdx = its OLD lastSnapshotIndex, below ours) fall through here and
|
||||||
|
# must be ignored: applying them would regress matchIndex/nextIndex and
|
||||||
|
# clear the reject streak mid-transfer, causing state flapping until the
|
||||||
|
# final reply lands.
|
||||||
node.matchIndex[peerId] = reply.matchIdx
|
node.matchIndex[peerId] = reply.matchIdx
|
||||||
node.nextIndex[peerId] = reply.matchIdx + 1
|
node.nextIndex[peerId] = reply.matchIdx + 1
|
||||||
node.snapRejectStreak.del(peerId)
|
node.snapRejectStreak.del(peerId)
|
||||||
|
|||||||
@@ -2931,6 +2931,36 @@ suite "Raft InstallSnapshot Send":
|
|||||||
check node.nextIndex["peer-1"] == 101
|
check node.nextIndex["peer-1"] == 101
|
||||||
check node.snapRejectStreak["peer-1"] == 2
|
check node.snapRejectStreak["peer-1"] == 2
|
||||||
|
|
||||||
|
test "intermediate chunk replies are ignored; final reply advances state":
|
||||||
|
var node = newRaftNode("leader", @["peer-1"])
|
||||||
|
node.currentTerm = 5
|
||||||
|
node.state = rsLeader
|
||||||
|
node.lastSnapshotIndex = 100
|
||||||
|
node.lastSnapshotTerm = 4
|
||||||
|
node.nextIndex["peer-1"] = 101
|
||||||
|
node.matchIndex["peer-1"] = 0
|
||||||
|
node.snapRejectStreak["peer-1"] = 2
|
||||||
|
node.snapPending.incl("peer-1")
|
||||||
|
|
||||||
|
# Intermediate chunk ack: follower replies success=true with its OLD
|
||||||
|
# lastSnapshotIndex (40 < our 100). Leader state must not move.
|
||||||
|
node.handleInstallSnapshotReply("peer-1", RaftMessage(
|
||||||
|
kind: rmkInstallSnapshotReply, term: 5, senderId: "peer-1",
|
||||||
|
success: true, matchIdx: 40))
|
||||||
|
check node.matchIndex["peer-1"] == 0
|
||||||
|
check node.nextIndex["peer-1"] == 101
|
||||||
|
check node.snapRejectStreak["peer-1"] == 2
|
||||||
|
check "peer-1" in node.snapPending
|
||||||
|
|
||||||
|
# Final reply: follower adopted the snapshot base (matchIdx == 100).
|
||||||
|
node.handleInstallSnapshotReply("peer-1", RaftMessage(
|
||||||
|
kind: rmkInstallSnapshotReply, term: 5, senderId: "peer-1",
|
||||||
|
success: true, matchIdx: 100))
|
||||||
|
check node.matchIndex["peer-1"] == 100
|
||||||
|
check node.nextIndex["peer-1"] == 101
|
||||||
|
check "peer-1" notin node.snapRejectStreak
|
||||||
|
check "peer-1" notin node.snapPending
|
||||||
|
|
||||||
test "InstallSnapshotReply term handling matches AppendEntriesReply":
|
test "InstallSnapshotReply term handling matches AppendEntriesReply":
|
||||||
var node = newRaftNode("leader", @["peer-1"])
|
var node = newRaftNode("leader", @["peer-1"])
|
||||||
node.currentTerm = 5
|
node.currentTerm = 5
|
||||||
|
|||||||
Reference in New Issue
Block a user