本篇文章主要介紹 TiDB 是如何使用分佈式一致性驗證框架 Jepsen 進行一致性驗證的。node
Jepsen 是由 Kyle Kingsbury 採用函數式編程語言 Clojure 編寫的驗證分佈式系統一致性的測試框架,做者使用它對許多著名的分佈式系統(etcd, cockroachdb...)進行了「攻擊」(一致性驗證),而且幫助其中的部分系統找到了 bug。這裏一系列的博客展現了做者的驗證過程以及對於一致性驗證的許多思考。git
Jepsen 驗證系統由 6 個節點組成,一個控制節點(control node),五個被控制節點(默認爲 n1, n2, n3, n4, n5),控制節點將全部指令發送到某些或所有被控制節點,這些指令包括底層的 shell 命令到上層的 SQL 語句等等。Jepsen 提供了幾個核心 API 用於驗證分佈式系統:github
DB算法
DB 封裝了所驗證的分佈式系統下載、部署、啓動和關閉命令,核心函數由 setup 和 teardown 組成,在 TiDB 的 Jepsen 測試中,setup 負責下載 TiDB 而且依次啓動 Placement Driver、TiKV 和 TiDB;teardown 負責關閉整個 TiDB 系統而且刪除日誌。sql
Clientshell
Client 封裝了每個測試所須要提供的客戶,每一個 client 提供兩個接口:setup 和 invoke,setup 負責對 TiDB 進行鏈接,而 invoke 則包含了測試中 client 對 TiDB 調用的 sql 語句,具體語句依測試而定。編程
Checkerbash
Checker 用於對測試生成的歷史進行驗證,判斷測試結果是否符合預期,歷史的格式以下圖所示:網絡
Nemesis併發
Nemesis 用於對系統引入故障,好比常見的網絡分區、網絡延時、節點宕機,在 TiDB 的測試中,有如下幾種 nemesis:
parts:網絡分區
majority-ring:每一個節點都看到不一樣的 majority
start-stop:對某些節點進行 SIGSTOP
start-kill:對某些節點進行 SIGKILL複製代碼
下圖展現了 parts nemesis 引入測試中後某些語句執行時出現了 time-out 的錯誤。
Generator
Generator 是 Jepsen 中的事件發生器,它將 Client 和 Nemesis 的操做交織在一塊兒,爲整個測試生成具體的執行語句。
TiDB 中的 Jepsen 測試有 3 個,分別是 bank、set 和 register 測試。
銀行測試用於驗證快照隔離。這個測試模擬了一個銀行系統中的各類轉帳,每一個銀行系統的初始能夠是這樣的:
[1 10]
[2 10]
[3 10]
[4 10]
[5 10]複製代碼
1-5 分別表明帳戶名稱,而 10 表明帳戶餘額。測試會隨機生成轉帳信息:
[1 2 5]複製代碼
表明將金額 5 從帳戶 1 轉入帳戶 2 這個操做。與此同時,測試會隨機讀取全部帳戶的存款信息,例如某一時刻帳戶的存款信息多是這樣的:
[8 14 2 11 15]複製代碼
下面是測試進行中的某次截圖:
在快照隔離下,全部的轉帳都必須保證每一時刻全部帳戶的總金額是相同的。TiDB 在即便引入了各類 nemesis 的狀況下仍舊順利地經過了測試。
這個測試從不一樣節點併發的將不一樣的數插入一張表中,而且進行一次最終的表讀取操做,用於驗證全部返回成功的插入值必定會出如今表中,而後全部返回失敗的插入值必定不在表中,同時,由於 nemesis 的引入,對於那些返回 time-out 的插入值,它們可能出現也可能不會出如今表中,這屬於正常狀況。
下面是測試進行中的某次截圖:
一樣,TiDB 經過了測試。
這個測試很好理解,建一個表,而後插入一條值,而後咱們把這個值看作是一個寄存器,而後在測試中併發地從各個節點對其進行 read、write 和 cas 操做。
而後利用 Jepsen 產生的一系列操做歷史(如上圖)進行 Linearizability 一致性驗證。這個算法是 Jepsen 的核心,也是 Jepsen 被業界所熟知的緣由之一,因此花時間去深刻學習了下,我會在另外一篇文章具體介紹這個算法。
每次 TiDB 更新代碼,咱們都會內部觸發 CI 來執行 Jepsen,經過 Jepsen 來保證 TiDB 的數據一致性。若是你對分佈式測試,一致性驗證感興趣,歡迎參與開發。
TiDB Jepsen:github.com/pingcap/jep…
徐鵬