# This test demonstrates the effect of delayed commit on a follower node after a # network hiccup between the leader and this follower. # Skip logging the boilerplate. Set up a raft group of 3 nodes, and elect node 1 # as the leader. Nodes 2 and 3 are the followers. log-level none ---- ok add-nodes 3 voters=(1,2,3) index=10 ---- ok campaign 1 ---- ok stabilize ---- ok # Propose a couple of entries. propose 1 data1 ---- ok propose 1 data2 ---- ok process-ready 1 ---- ok # The interesting part starts below. log-level debug ---- ok deliver-msgs 2 3 ---- 1->2 MsgApp Term:1 Log:1/11 Commit:11 Entries:[1/12 EntryNormal "data1"] 1->2 MsgApp Term:1 Log:1/12 Commit:11 Entries:[1/13 EntryNormal "data2"] 1->3 MsgApp Term:1 Log:1/11 Commit:11 Entries:[1/12 EntryNormal "data1"] 1->3 MsgApp Term:1 Log:1/12 Commit:11 Entries:[1/13 EntryNormal "data2"] process-ready 3 ---- Ready MustSync=true: Entries: 1/12 EntryNormal "data1" 1/13 EntryNormal "data2" Messages: 3->1 MsgAppResp Term:1 Log:0/12 3->1 MsgAppResp Term:1 Log:0/13 # Suppose there is a network blip which prevents the leader learning that the # follower 3 has appended the proposed entries to the log. deliver-msgs drop=(1) ---- dropped: 3->1 MsgAppResp Term:1 Log:0/12 dropped: 3->1 MsgAppResp Term:1 Log:0/13 # In the meantime, the entries are committed, and the leader sends the commit # index to all the followers. stabilize 1 2 ---- > 2 handling Ready Ready MustSync=true: Entries: 1/12 EntryNormal "data1" 1/13 EntryNormal "data2" Messages: 2->1 MsgAppResp Term:1 Log:0/12 2->1 MsgAppResp Term:1 Log:0/13 > 1 receiving messages 2->1 MsgAppResp Term:1 Log:0/12 2->1 MsgAppResp Term:1 Log:0/13 > 1 handling Ready Ready MustSync=false: HardState Term:1 Vote:1 Commit:13 CommittedEntries: 1/12 EntryNormal "data1" 1/13 EntryNormal "data2" Messages: 1->2 MsgApp Term:1 Log:1/13 Commit:12 1->3 MsgApp Term:1 Log:1/13 Commit:12 1->2 MsgApp Term:1 Log:1/13 Commit:13 1->3 MsgApp Term:1 Log:1/13 Commit:13 > 2 receiving messages 1->2 MsgApp Term:1 Log:1/13 Commit:12 1->2 MsgApp Term:1 Log:1/13 Commit:13 > 2 handling Ready Ready MustSync=false: HardState Term:1 Vote:1 Commit:13 CommittedEntries: 1/12 EntryNormal "data1" 1/13 EntryNormal "data2" Messages: 2->1 MsgAppResp Term:1 Log:0/13 2->1 MsgAppResp Term:1 Log:0/13 > 1 receiving messages 2->1 MsgAppResp Term:1 Log:0/13 2->1 MsgAppResp Term:1 Log:0/13 # The network blip prevents the follower 3 from learning that the previously # appended entries are now committed. deliver-msgs drop=(3) ---- dropped: 1->3 MsgApp Term:1 Log:1/13 Commit:12 dropped: 1->3 MsgApp Term:1 Log:1/13 Commit:13 # The network blip ends here. status 1 ---- 1: StateReplicate match=13 next=14 2: StateReplicate match=13 next=14 3: StateReplicate match=11 next=14 inflight=2 # The leader still observes that the entries are in-flight to the follower 3, # since it hasn't heard from it. Nothing triggers updating the follower's # commit index, so we have to wait up to the full heartbeat interval before # the leader sends the commit index. tick-heartbeat 1 ---- ok # However, the leader does not push the real commit index to the follower 3. It # cuts the commit index at the Progress.Match mark, because it thinks that it is # unsafe to send a commit index higher than that. process-ready 1 ---- Ready MustSync=false: Messages: 1->2 MsgHeartbeat Term:1 Log:0/0 Commit:13 1->3 MsgHeartbeat Term:1 Log:0/0 Commit:11 # Since the heartbeat message does not bump the follower's commit index, it will # take another roundtrip with the leader to update it. As such, the total time # it takes for the follower to learn the commit index is: # # delay = HeartbeatInterval + 3/2 * RTT # # This is suboptimal. It could have taken HeartbeatInterval + 1/2 * RTT, if the # leader sent the up-to-date commit index in the heartbeat message. # # See https://github.com/etcd-io/raft/issues/138 which aims to fix this. stabilize 1 3 ---- > 3 receiving messages 1->3 MsgHeartbeat Term:1 Log:0/0 Commit:11 > 3 handling Ready Ready MustSync=false: Messages: 3->1 MsgHeartbeatResp Term:1 Log:0/0 > 1 receiving messages 3->1 MsgHeartbeatResp Term:1 Log:0/0 > 1 handling Ready Ready MustSync=false: Messages: 1->3 MsgApp Term:1 Log:1/13 Commit:13 > 3 receiving messages 1->3 MsgApp Term:1 Log:1/13 Commit:13 > 3 handling Ready Ready MustSync=false: HardState Term:1 Vote:1 Commit:13 CommittedEntries: 1/12 EntryNormal "data1" 1/13 EntryNormal "data2" Messages: 3->1 MsgAppResp Term:1 Log:0/13 > 1 receiving messages 3->1 MsgAppResp Term:1 Log:0/13