跑三小时的monkey测试该怎么算_百亿次的锤炼 - 带逛Dragonboat的各类测试
本文以近期開源的Dragonboat多組Raft庫為例,介紹Dragonboat這樣一個典型分布式系統是如何做測試的。Dragonboat以Go實現,能在普通硬件上提供每秒1000萬次以上的強一致讀寫,它是目前github.com上速度最快的功能完整的多組Raft開源庫。歡迎大家試用,并請點Star支持:
lni/dragonboat?github.com最大的誤導
常看到有系統吹捧自己可靠的方法是說某大型活動用了它,或說是某某公司某內部項目用了,從而得出可靠的結論,生產環境儼然成了廉價公關軟文口中的測試平臺。其實眾所周知,某活動全場意外當機重啟的節點數少之又少,磁盤毀損一整年才2-4%,而故障性的網絡分區在很多DevOps崗的整個職業生涯里也就遇到幾次而已,以至于多年來各一線公司網絡分區造成故障的故事收集起來也才寫滿幾頁紙。某活動扛住了或是某項目用了,這些完全不是軟件可靠與否的充要條件。
事實其實是殘酷的。曾閱讀過國內排名前四的某司一個共識庫,30分鐘代碼讀下來找到多處數據丟失毀損的bug,實現則是很典型的那種打死也不肯寫測試的全裸奔模式。
對于軟件,任何無法用代碼來驗證的廉價營銷式說辭,不說、不看、不聽相較于廉價的文宣,Dragonboat對系統正確性滿心敬畏,老老實實以完備的測試方案、開源的測試代碼、公開可驗證的測試結果數據三者來提供切實的保障。
常規測試
常規測試部分,Dragonboat做了:
- 數萬行全手寫測試代碼,Raft協議3000行核心代碼擁有過萬行測試代碼護航
- Go內建的race detector測試
- 各類靜態檢查,及早發現如錯誤返回值未處理等可能問題
- 使用go-fuzz對所有網絡與本地輸入做隨機輸入fuzz測試
Dragonboat內各Package覆蓋率基本均在90%以上。以Raft協議實現部分為例,基本不正確地刪改一行代碼就能觸發多個測試錯誤。這些測試代碼,連同下面要介紹的monkey testing,nightly build時在race detector被打開的情況下運行。對于長期被人詬病的Golang的error處理方式,的確容易因為人為疏忽造成error返回有漏檢的可能,但僅gometalinter一個軟件收錄的靜態檢測工具就有多種能對付它。對各輸入的Fuzz testing初聽來或許有些多此一舉,但一跑Fuzz testing幾十秒就發現bug的例子比比皆是,Dragonboat開發中也曾遇到。
基于Raft協議的自測
Raft協議對內部數據有嚴格限定要求,比如顯而易見的就有:
- 所有entry的Index值始終應該是連續且嚴格遞增的
- 所有entry的Term值應該是單向的
- 當Index與Term確定時,entry內容是唯一確定的
這些都提供較小代價下運行時自測的機會。僅以第一項為例,在Dragonboat中它被落實到多個點位上,對應用透明的進行自測:
- 在節點完成了協議規定的檢查,即將append log時
- 在entry被commit以后,準備由Raft協議返回供復制狀態機執行時
- 復制狀態機即將執行entry,由該entry Index對比當前最新已執行的entry的Index值時
這些自測在Dragonboat中無法通過任何設置予以關閉,甚至在Benchmark跑分時也嚴格限定必須進行。
磁盤文件IO測試
磁盤文件IO要做正確有多難,可以先看兩個事實:
- Golang的標準庫在MacOS上默認的使用,基本必然出現丟數據
- 專業測試顯示,包括git、leveldb、ZooKeeper等最著名項目,丟數據的bug曾有一堆
假設文件系統的可靠是天經地義的吧?很遺憾,這種假設也是高危動作。
TS Pillai的這張總結圖表直觀顯示文件IO做正確有多難為解決這些磁盤文件IO的挑戰,Dragonboat首先選擇不自作聰明的到處自己去做文件操作,把存儲盡可能交給RocksDB,并對基于RocksDB的系統加以各類測試:
- 使用ScyllaDB的charybdefs實現的磁盤錯誤注入測試
- 使用自動開關的掉電數據完整性測試
前者可以模擬諸如RocksDB試圖讀一個sst文件的內容時第二次讀操作返回錯誤,幫助檢查Dragonboat是否按照設計正確地處理這樣的IO錯誤。掉電測試檢查fsync是否被正確配置(如MacOS上是否fcntl(fd, F_FULLFSYNC)了)與調用,是否IO邏輯上有丟數據的問題。
看了上述介紹,可能有人覺得這是小題大做,從Turbo C就開始玩的文件操作有啥難?hehe,文件操作方面是git、ZooKeeper的作者經驗多,還是您更牛?
as we know, there are known knowns; there are things we know we know. We also know there are known unknowns; that is to say we know there are some things we do not know. But there are also unknown unknowns—the ones we don't know we don't know.Donald Rumsfeld
Monkey Testing
Monkey Testing有時也稱為Chaos Engineering,目的在于自動測試系統在各組件失效當機情況下系統是否依舊能按設計提供應有的服務。與Fuzz testing的隨機數據輸入不同,Monkey Testing / Chaos Engineering著眼于隨機破壞性事件對系統的影響。
Netflix提供了大量公開的資料,推廣Chaos Engineering在Dragonboat的monkey testing中,各種隨機破壞性事件的組合被注入到一個多節點的測試環境里,在一年多的自動測試期間,導致了百億數量級次數的Raft節點重啟事件,發現并修正了大量Raft協議實現與相關輔助功能的bug。具體的,在monkey testing中,被注入的隨機事件有:
- 隨意的停止各節點
- 隨意刪除節點所有Raft數據
- 隨意丟棄傳輸中的消息
- 隨意網絡分割節點暫時阻斷通訊
在上述大量注入的隨機破壞性事件前提下,同時在上述多節點測試環境上運行大量Raft組實例,進行Raft的讀寫測試。該monkey testing環境同時內建一組三個節點的Drummer系統,三個Drummer節點觀測、維護各Raft組健康信息,并在發現Raft組的成員失效以后,試圖在其它節點上通過Raft組成員變更,新增并啟動一個新的Raft成員,替換已失效的Raft成員。
上述三節點的Drummer本身也是一個基于Dragonboat的Raft實現的無單點系統,且在monkey testing中同樣會被注入上述隨機錯誤。Drummer的上述監控、修復Raft組的業務邏輯是在自身同樣面對大量被注入的隨機破壞性事件的前提下完成的,這進一步驗證了此類具體實際業務邏輯下,Dragonboat的Raft實現的可靠性。
在一個節點平均存活僅幾分鐘的情況下,在幾臺服務器上每晚便可完成千萬次量級的節點隨機失效與重啟測試。在此及其嚴酷的測試環境中,同時向系統施加Raft讀寫請求,配合大量后臺的Raft快照保存與快照恢復操作,嚴格的后驗檢查確保:
- Jepsen的Knossos和porcupine檢查,絕無違反稱為linearizability的強一致性
- Raft組在有Quorum的時候需可用
- 用戶應用狀態機狀態一致
- Raft組成員一致
- 磁盤上保存的Raft Entry Log一致
一部分Jepsen可讀格式的edn log已被公布,可供大家使用各自選定的Linearizability checker檢驗:
lni/knossos-data?github.com版權信息:
本文歡迎在不做任何修改刪節、保留完整原作者信息與出處的前提下合理轉載。
總結
以上是生活随笔為你收集整理的跑三小时的monkey测试该怎么算_百亿次的锤炼 - 带逛Dragonboat的各类测试的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 演讲者模式投影到幕布也看到备注_家用投影
- 下一篇: 如何制作印章_电子公章怎么制作