選舉演算法 [5X6Y]

這裡簡要紀錄選舉演算法的想法

節點角色 [local-0]

節點的角色應該是以下三種之一

  • Follower
  • Candidate
  • Leader

我們希望leader至多只有一個,只有有leader的集群才會對外提供服務

任期 [local-1]

任期可以叫term或是generation clock,用來這是第幾輪的決策,每個節點都要有這個資訊

日誌 [local-2]

日誌的每個entry應該包含

  • 索引(index),每次新增entry都應該嚴格遞增的數字
  • 任期(term),entry寫入時的任期
  • 指令(command),要進行什麼操作

狀態轉換 [local-5]

節點啟動時可以不知道leader是誰,我們可以設定為

  • term為1
  • 角色為follower
  • 設定定時任務檢查leader發送的heartbeat訊號

如果定時任務歸零時沒有收到leader發送的heartbeat訊號,就發起選舉;反之,重設定時任務

發起選舉 [local-3]

發起選舉可以定義為

  1. 把節點的term加一
  2. 轉換為candidate
  3. 投給自己
  4. 要求其他節點投給自己
  5. 等待有限時間N

現在會有幾種可能

  • 在N時間內有過半節點投給自己。這時候表示自己就是leader,轉換狀態並設定循環任務不斷發送heartbeat訊號給所有節點
  • 在N時間後沒有得到過半節點的票,逾時再次發起選舉(回到最前面)
  • 在N時間內收到leader廣播的heartbeat,而且這個leader的term比自己大。這時候就轉變為follower,更新term並紀錄誰是leader
  • 在N時間內收到其他candidate要求投給它的投票訊息,而且該投票訊息的term比自己大。這時候也轉為follower,更新term

由於candidate把term加一了才繼續,舊的領導者(比如斷線之後再連回來)的heartbeat就不會影響candidate跟其他節點

處理投票請求 [local-4]

收到投票請求的時候需要按照以下順序決策,以下的同意就表示同時要轉換為follower狀態

  1. candidate的term小於當前節點的term:回覆拒絕
  2. candidate的term大於當前節點的term:回覆同意
  3. candidate的term跟當前節點一樣時要繼續攤開
    1. 已經投過票了:請求candidate就是已經投過的candidate,回覆同意;反之回覆拒絕
    2. candidate的log index大於自己:回覆同意
    3. 其餘情況都回覆拒絕

之所以要看log index,是要避免狀態比較落後的節點成為leader,以免資料遺失

原則上就是

  • 任期高者不投給任期低者
  • log index高者不投給log index低者
  • 每個任期只投一張票(否則過半就沒有意義)

leader如果直接看到比自己大的term(無論是heartbeat還是投票訊息),都應該放棄leader身份轉換為follower

現在我們來看逾時後投票的效果