元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

Roblox為其平臺上5000萬要求極高的青少年和青春期前的兒童提供遊戲製作服務。

元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

本週,該公司釋出了一份內容冗長、極其詳細的事後分析報告,描述了去年持續整整三天的重大故障事件,所有從事企業基礎架構工作的人都應該認真讀一讀。

Roblox聲稱:“無論持續時間還是複雜程度,這次故障都是獨一無二的。”而這種說法未免輕描淡寫。在網際網路上,三天這段時間實在太長了;去年10月的一天,Facebook宕機了短短几小時,全世界就一度為之抓狂。

Roblox管理自己的基礎架構,這對於一家成立於2004年的公司來說並不罕見。

該公司在這套基礎架構上擁有的伺服器超過18000臺,還部署和管理自己的儲存裝置和網路裝置。

它廣泛依賴HashiCorp公司開發的技術,包括 Nomad、Vault和Consul。

Consul 是一類名為服務網格(service mesh)的新興企業技術的一部分,它在幫助釐清導致這起故障的情形方面發揮了關鍵作用。

與大多數故障一樣,這次故障一開始時是無害的,但隨後在用於執行Roblox基礎架構的軟體層的深處發現了一個新的錯誤(bug)。

像Consul這樣的服務網格其功能類似網路上的交通管制員,允許各個微服務相互通訊,並交換完成工作所需要的資料。

乍一看,這似乎只是執行Consul叢集的硬體出現的簡單故障,但更換所有伺服器後,效能依然受到影響。

由Roblox工程師和HashiCorp工程師組成的聯合團隊最終查明,在Consul核心一個名為BoltDB的開源日誌記錄專案在設計上所做的選擇導致了瓶頸,而這完全是因Roblox的獨特架構而暴露出來的。

之所以花那麼長的時間來診斷問題,一方面原因是團隊無法確定導致問題的到底是Roblox的選擇,還是Consul內部某個存在缺陷的元件。事實證明,這兩個因素多少都有所牽涉。

在過去的三個月間,Roblox對其基礎架構進行了數次更改。

該公司表示:“在一個Consul叢集上執行所有Roblox後端服務使我們遇到了這種性質的故障。”因此,它增加了第二個資料中心來執行後端服務,還計劃在那些資料中心區域裡面實施可用區(AZ)。

HashiCorp還在開發新版本的Consul,以取代BoltDB。

但是儘管如此,三大雲提供商的銷售代表還是沒有從Roblox獲得任何大筆的新業務。

“總的來說,我們發現,公共雲對於並不注重效能和延遲,且在規模有限的環境下執行的應用程式來說是一種很好的工具。然而,針對我們那些非常注重效能和延遲的工作負載,我們所做的選擇是在本地構建和管理我們自己的基礎架構,”該公司表示。

衷心感謝Roblox對這起可能是該公司發展史上最嚴重的事件之一進行了如此詳盡細緻的分析。不過幸運的是,其核心受眾很快就忘了這荏事。

元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

BoltDB問題似乎直接歸咎於糟糕的設計。需要空閒連結串列(freelist)很好,在每次追加後都需要將整個空閒連結串列同步到磁碟很可笑很荒唐。

我是BoltDB的開發者。是的,這是糟糕的設計。該專案從未打算投入到生產環境中,而是作為LMDB的移植版,因此我可以理解其內部結構。我簡化了空閒連結串列的處理,因為這是一個小兒科專案。當時(2014年前後)Shopify在LMDB或Go驅動程式方面遇到了一些嚴重的問題,幾個月後我們還是無法解決,於是我們換成了Bolt。遺憾的是,我這個糟糕的設計仍然存在。

LMDB使用常規bucket用於空閒連結串列,而Bolt只是將該連結串列作為陣列來儲存。它在相當大的程度上簡化了邏輯,在大多數使用場合下通常不會引發問題。只有當有人寫入了大量資料,然後刪除資料、從不使用這些資料時,才會出現這個問題。Roblox聲稱有4G的閒置頁面,這意味著一個含有4位元組頁面數的龐大陣列。

元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

我認為設計方面的選擇該由我來負責,但是與大多數開源軟體(OSS)軟體一樣,責任還是在於終端使用者。看到一個錯誤給其他人帶來這麼大的麻煩總是糟透了。

至於HashiCorp,他們是一群很出色的人。沒有幾個開發人員比他們的CTO Armond Dadger更受本人尊敬的了。他是個絕頂聰明的傢伙。話雖如此,實際生產環境中還是有很多不定因素,有時候錯誤還是趁虛而入。

Roblox公告故障「根本原因」部分:

幾個月前,我們在自己的一部分服務上啟用了新的Consul流式傳輸(streaming)功能。這項功能旨在降低Consul叢集的CPU使用量和網路頻寬,它按預期的方式正常工作,因此在接下來的幾個月,我們逐步在更多的後端服務上啟用了該功能。

10月27日14:00,即出現故障前一天,我們在負責流量路由的後端服務上啟用了該功能。

作為這次部署的一個方面,為了準備迎接我們通常在年底看到的流量增加現象,我們還將支援流量路由的節點數量增加了50%。

在故障事件開始前一天,整個系統在這個層級的流式傳輸方面執行良好,因此起初並不清楚為什麼效能發生了變化。

然而,我們對來自Consul伺服器的效能報告和火焰圖(flame graph)進行分析後,看到了表明引起資源爭用的流式傳輸程式碼路徑導致了CPU使用量高的證據。

於是,我們禁用了所有Consul系統的流式傳輸功能,其中包括流量路由節點。

配置更改在15:51完成傳播,此時Consul KV寫入的第50個百分位降低到了300ms。我們終於取得了突破。

為什麼流式傳輸是個問題?

HashiCorp解釋道,雖然流式傳輸總體上更高效,但與長輪詢(long polling)相比,它在實現中所用的併發控制元素(Go通道)較少。在負載非常高的情況下(具體來說,讀取負載和寫入負載都非常高),流式傳輸的設計加劇了單單一條Go通道上的資源爭用程度,這導致寫入期間出現阻塞,從而大幅降低效率。

這種行為也解釋了核心數量更多的伺服器帶來的影響:這些伺服器是採用NUMA記憶體模型的雙插槽架構。因此,在這種架構下,共享資源的額外爭用現象變得更嚴重。我們關閉流式傳輸後,顯著改善了Consul叢集的健康狀況。

儘管取得了這一突破,但我們還是沒有擺脫困境。

我們看到Consul間歇性地選舉新的叢集主節點(leader),這很正常,但我們也看到一些主節點表現出了與我們在禁用流式傳輸之前看到的同樣的延遲問題,而這不正常。在沒有任何明顯的線索表明主節點速度緩慢問題的根本原因這一情況下,並且又有證據表明只要某些伺服器沒有被選為主節點,整個叢集就健康執行,於是團隊做出了務實的決定,透過防止有問題的主節點保持處於被選舉的狀態,從而規避這個問題。這使團隊能夠專注於將依賴Consul的Roblox服務恢復到健康狀態。

但是那些速度緩慢的主節點發生了什麼?

我們在故障事件期間沒有弄清楚這一點,但HashiCorp的工程師在故障後的幾天內查明瞭根本原因。Consul使用了一個名為BoltDB的流行的開源永續性庫來儲存Raft日誌。它並不用於儲存Consul裡面的當前狀態,而是滾動記錄所採用的操作。為了防止BoltDB無限止地變大,Consul定期執行快照。快照操作將Consul的當前狀態寫入到磁碟,然後從BoltDB中刪除最舊的日誌條目。

但是,由於BoltDB的設計使然,即使明明已刪除了最舊的日誌條目,BoltDB在磁碟上使用的空間也不會縮小。相反,所有用於儲存已刪除資料的頁面(檔案中的4kb段)而是都被標記為“空閒”,並重新用於後續的寫入。BoltDB在一種名為“空閒連結串列”(freelist)的結構中跟蹤這些空閒頁面。通常,寫入延遲並不受更新空閒連結串列所需的時間的顯著影響,但Roblox的工作負載暴露了BoltDB中一個病態的效能問題,從而使得空閒連結串列維護起來開銷極大。

元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

在Roblox的Consul的正常運作情況

元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

16:35 PST玩家數量減少期間的CCU

元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

Roblox

隨後用上面所示的perf報告顯示了該內容。絕大多數時間花費在了透過流式傳輸訂閱程式碼路徑的核心自旋鎖上。

元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

HTOP顯示了128個核心上的CPU使用情況

元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

BoltDB空閒連結串列操作分析

元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

分析中所使用的詳細的BoldDB統計資訊

元宇宙第一股 Roblox 整整癱瘓了三天:因 BoltDB 糟糕的設計

深入研究TCP零視窗。當TCP接收方的緩衝器開始填充時,它會縮小接收視窗。如果它填滿,會將視窗縮小至0,這會命令TCP傳送端停止傳送。

故障報告英文原文:https://blog。roblox。com/2022/01/roblox-return-to-service-10-28-10-31-2021/

頂部