程序暫停、Fencing 與系統模型 (Process Pauses & Fencing)

★★★★Draft

FromDDIA

📚 From the Books

程序暫停、Fencing 與系統模型 (Process Pauses & Fencing)Distributed Troubles

一個節點可能在任意一行程式碼中間被凍結十幾秒——GC、VM 遷移、換頁都會這麼幹——而它自己毫不知情。醒來後它以為只過了一瞬,世界卻已經改選了新 leader。這一頁講的是:既然節點連「自己還是不是 leader」都無法確定,正確性要靠什麼守住。

🧠 Intuition

TIP

節點無法確知任何事,只能從(不)收到的訊息去猜。所以正確性不能建立在「我以為我還持有鎖」之上,而要建立在兩件外部事實上:多數決 (quorum) 決定誰還活著、fencing token 讓過期者的寫入被資源端當場擋下。

⚖️ Tradeoffs

真相由多數決定 (the truth is defined by the majority)
  • 半斷線的節點就算發現「我的訊息沒被回應」,也無力改變其他人的判決;經歷長 GC 的節點恢復後根本不知道已過了一分鐘。
  • 所以節點不能信任自己對局勢的判斷。許多演算法靠 quorum 投票:決策要最低數量的節點同意。若 quorum 判你死了,即使你自認活著也得服從。
  • 最常用絕對多數(過半),因為一個系統裡只會有一個多數,不會冒出兩個多數做出互相矛盾的決策。
Fencing token:擋下過期者的寫入
  • 真實 bug(HBase 曾發生):Client 1 拿到租約後長 GC、租約過期;Client 2 拿到同一租約並寫入;Client 1 恢復後仍以為租約有效也寫入同一檔 → 資料損毀
  • 解法 fencing:鎖服務每次發鎖都附一個單調遞增的 fencing token;每個寫入都要帶上當前 token。
  • 運作:Client 1 帶 token 33、暫停、租約過期;Client 2 帶 token 34 寫入成功;Client 1 恢復後帶 token 33 來寫,資源端發現「已處理過更高的 34」→ 拒絕 33。ZooKeeper 的 zxid / cversion 可當 token。
  • 關鍵紀律:檢查必須在資源端做、主動拒絕過時 token;不能只靠客戶端自律——服務不該假設客戶端總是乖的。
拜占庭故障:從「不可靠但誠實」到「會說謊」
  • fencing 能擋無意出錯的節點(不知租約過期),但擋不住刻意偽造 token 的節點。
  • 本書的預設假設是節點不可靠但誠實 (unreliable but honest):可能慢、可能狀態過時,但只要回應就照協議說實話。若節點會說謊(傳任意錯誤/偽造回應),就是拜占庭故障 (Byzantine fault),此時達成共識即拜占庭將軍問題
  • 值得付出拜占庭容錯成本的場景很少:航太(輻射翻轉記憶體)、多方互不信任的系統(如區塊鏈)。一般資料中心節點同屬一個組織、可合理信任,拜占庭協議成本過高。
  • 但**弱形式的「說謊」**仍要防:封包被硬體損壞(TCP/UDP checksum 偶爾漏抓)、輸入要驗證、NTP 查多台伺服器排除離群值。
系統模型:拿什麼假設去證明正確性
  • 時序模型三種:同步(延遲/暫停/時鐘誤差都有上界——不現實但好推理)、部分同步(多數時候像同步、偶爾超界——最貼近現實)、非同步(不做任何時序假設、甚至沒有時鐘——最受限)。
  • 節點故障模型三種:crash-stop(掛了就永不回來)、crash-recovery(可恢復,持久儲存在崩潰間保留、記憶體遺失)、Byzantine(任意行為)。真實系統通常用 部分同步 + crash-recovery 這組。
  • 安全性 (safety) vs 活性 (liveness):safety 是「壞事永不發生」(如 fencing token 的唯一與遞增),一旦違反可指出確切時刻且無法挽回;liveness 是「好事終會發生」(如請求終會有回應)。分散式演算法通常要求 safety 在任何情況都成立,而 liveness 只在附加條件下保證(如多數存活且網路最終恢復)。

🔑 Takeaways

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading