網路不可能一直處於正常情況,因為Leader或者某個Follower有可能會崩潰,從而導致日誌不能一直保持一致。因此存在以下三種情況:
(1)Follower缺失當前Leader上存在的日誌條目。
(2)Follower存在當前Leader不存在的日誌條目。(比如舊的Leader僅僅將AppendEntries RPC訊息傳送到一部分Follower就崩潰掉,然後新當選Leader的伺服器恰好是沒有收到該AppendEntries RPC訊息的伺服器)
(3)Follower即缺失當前Leader上存在的日誌條目,也存在當前Leader不存在的日誌條目
圖中最上方是日誌的索引號(1-12),每個方塊代表一條日誌資訊,方塊內數字代表該日誌所處的任期號。
圖中當前Leader(圖中最上方一行日誌代表當前Leader日誌)處於任期號為8的時刻。以此圖說明以上三種情況存在的原因:
Follower a、b(Follower崩潰沒有接收到Leader傳送的AppendEntries RPC訊息)滿足以上說明的第一種情況。
Follower c在任期為6的時刻,Followerd在任期為7的時刻為Leader,但沒有完全完成日誌的傳送便崩潰了,滿足以上說明的第三種情況。
Follower e在任期為4的時刻,Followerf在任期為2、3的時刻為Leader,但沒有完全完成日誌的傳送便崩潰了,同時在其他伺服器當選Leader時刻也沒有接收到新的Leader傳送的AppendEntries RPC訊息,滿足第三種情況。
日誌不一致的解決方案
Leader通過強迫Follower的日誌重複自己的日誌來處理不一致之處。這意味著Follower日誌中的衝突日誌將被Leader日誌中的條目覆蓋。因此Leader必須找到與Follower最開始日誌發生衝突的位置,然後刪除掉Follower上所有與Leader發生衝突的日誌,最後將自己的日誌傳送給Follower以解決衝突。
Leader不會刪除或覆蓋自己本地的日誌條目
這些步驟從之前說到的日誌的一致性檢查開始。
當發生日誌衝突時,Follower將會拒絕由Leader傳送的AppendEntries RPC訊息,並返回一個響應訊息告知Leader日誌發生了衝突。
Leader為每一個Follower維護一個nextIndex值。該值用於確定需要傳送給該Follower的下一條日誌的位置索引。(該值在當前伺服器成功當選Leader後會重置為本地日誌的最後一條索引號+1)
當Leader瞭解到日誌發生衝突之後,便遞減nextIndex值。並重新傳送AppendEntries RPC到該Follower。並不斷重複這個過程,一直到Follower接受該訊息。
一旦Follower接受了AppendEntries RPC訊息,Leader則根據nextIndex值可以確定發生衝突的位置,從而強迫Follower的日誌重複自己的日誌以解決衝突問題。
情況a:如上圖,伺服器S1在任期為2的時刻僅將日誌<index:2,term:2>傳送到了伺服器S2便崩潰掉。
情況b:伺服器S5在任期為3的時刻當選Leader(S5的計時器率先超時,遞增任期號為3,高於伺服器S3和S4,因此可以當選Leader),但沒來得及傳送日誌便崩潰掉。
情況c:伺服器S1在任期為4的時刻再次當選Leader(S1重啟時,任期仍然為2,收到新的LeaderS5傳送的心跳資訊後更新任期為3,而在LeaderS5崩潰後,伺服器S1為第一個計時器超時的,因此發起投票,任期更新為4,大於網路中其他伺服器任期,成功當選Leader),同時將日誌<index:2,term:2>傳送到了伺服器S2和S3,但還沒有通知伺服器對日誌進行提交便崩潰掉。
情況d:情況(a->d)如果在任期為2時伺服器S1作為Leader崩潰掉,S5在任期為3的時刻當選Leader,由於日誌<index:2,term:2>還沒有被複制到大部分伺服器上,並沒有被提交,所以S5可以通過自己的日誌<index:2,term:3>覆蓋掉日誌<index:2,term:2>。
情況e:情況(a->e)如果在任期為2時伺服器S1作為Leader,並將<index:2,term:2>傳送到S2和S3,成功複製到大多數成員伺服器上。並且成功提交了該日誌,那麼即便S1崩潰掉,S5也無法成功當選Leader,因為S5不具備網路中最新的已被提交的日誌條目。