三個Rust程式碼庫的經驗分享:何時開始使用Rust? - convex

banq發表於2021-11-25

什麼時候是開始使用 Rust 的好時機?

Convex 的創始團隊有幸領導了世界上一些最常用的基於 Rust 的系統的開發:

  • Magic Pocket,Dropbox 的地理分散式資料儲存系統。該系統已在近百萬個併發儲存節點上執行,並直接管理 EB 的磁碟儲存,所有這些都沒有發生任何重大事件。
  • Nucleus,Dropbox 同步引擎的全新改寫。該程式碼庫在超過 50 億臺裝置上執行,管理數萬億個檔案,應對各種奇怪的終端使用者檔案系統配置,並具有無可挑剔的可靠性記錄。
  • Convex,零設定、無限可擴充套件的後端,專為響應式應用程式開發人員的需求而設計。好的,這還不是最常用的程式碼庫之一(目前)。這是我們現在正在建設的。

實際上,在 Rust 1.0 釋出之前,我們在 2014 年就在積極使用它!該語言已經走了很長一段路,現在採用的障礙顯著降低。然而,儘管我們在使用 Rust 方面有著非常積極的經驗,但我們並不總是向每個開發團隊推薦它。在這篇文章中,我們希望為 Rust 非常適合專案的條件提供一些參考。

 

為什麼要切換?

如果您正在閱讀本文,您可能不需要對 Rust 的優勢有太多的說服力。Rust 一直是我們所從事的一些專案取得成功的重要貢獻者。

  • 效能

像魔術師口袋裡的專案在執行,認真大而昂貴的艦隊伺服器。我們在該專案中的目標之一是儘可能地降低成本開銷,其中“開銷”通常定義為“任何非硬碟驅動器”。切換到 Rust,由於缺乏執行時和對記憶體分配的嚴格控制而具有可預測的利用率,這使我們能夠顯著減少儲存節點中的 CPU 和 RAM 量,而不會像我們在早期 Go 程式碼庫中遇到的 OOM 問題的持續威脅。最終,Rust 在一個更安全、更符合人體工程學的開發環境中為我們提供了 C++ 級別的控制和效率。

  • 正確性

為數億使用者無縫遷移 EB 資料或同步狀態是可怕的。像這樣的專案對正確性的要求非常高。Rust 的強型別系統和借用檢查器是用於開發正確的多執行緒程式碼的巨大的開箱即用資源。

Nucleus 專案大量利用代號為 Trinity 的測試框架,該框架確定性地驗證了執行執行緒複雜交錯的不變數。Rust 對期貨的支援以及實現我們自己的對抗性排程程式的自由使這個專案成為可能,而在另一種語言中,尤其是具有語言執行時的語言,它會困難得多。

我們將繼續推進 Convex 測試分散式和併發系統的最新技術。

  • 生產率

雖然 Rust確實有一個陡峭的學習曲線,但幾乎所有與我們合作過的工程師都經歷過使用它後生產力的提高。避免型別錯誤和資料競爭是複雜專案的巨大生產力提升。

Rust 庫也往往具有極高的質量,Cargo 提供的構建系統使處理大型 單體倉庫monorepos 變得輕而易舉。我們承認,我們已經浪費了無數時間來處理 npm 中的構建系統異常或與 Bazel 搏鬥,而 Cargo 根本不是這種情況。

用 Rust 程式設計的一個潛在的意想不到的好處是,它實際上讓我們成為了更好的原型!過去,在用 Go 重寫用於生產之前,我們已經用 Python 等語言構建了分散式系統的原型。拉斯特重構是如此容易得多,也使我們能夠發展成原型完全成熟的生產系統,而無需重寫。重構通常就像進行更改然後追蹤編譯器錯誤的尾部一樣簡單,直到它完成。Rust 擁有我們使用過的任何語言中最有用的編譯器建議。

  • 多功能性

Rust 不依賴於語言執行時的事實使其成為需要嵌入其他系統的程式碼的絕佳選擇。例如,Unicode 規範化是一個非常複雜且容易出錯的過程。當我們需要跨 Python、Go 和 Rust 實現相同的規範化演算法時,我們為開源 Rust 實現做出了貢獻,並在所有三種語言中嵌入了相同的程式碼。

在其他語言中嵌入 Rust 很容易,但在 Rust 中嵌入其他語言也很容易。Convex 允許客戶將資料庫查詢表達為成熟的 JavaScript/TypeScript 函式,這些函式在我們的 Rust 後端的 V8 隔離中執行。我們的團隊對WASM以及 Rust 與 WASM 團隊之間的密切聯絡感到特別興奮。我們期待著在 Convex 中安全嵌入其他語言的未來機會。

 

什麼是工作的最佳工具

大公司經常發生的爭論之一是對一致性的渴望,還是讓工程師使用最好的工具來完成工作。想要使用可以讓您個人更有效率的語言或加入熱門新事物是很容易的。不幸的是,這通常以組織成本為代價。本地決策可能對工程產生廣泛的影響,通常您的程式碼在您離開後仍然存在很長時間。可維護性通常也比大型專案的初始開發工作更重要。這種緊張關係一直是我們團隊對以前專案的爭論的根源。

  • 開發者可替代性

Rust 是一門難學的語言。根據我們的經驗,開發人員通常可以在 Python、Go、Java 等之間跳轉,並通過模式匹配他們的成功之路,即使他們不精通該語言。開發人員第一次與 Rust 中的借用檢查器發生衝突或不得不對 trait 物件進行推理時,需要一些實際的專門學習時間。

入門問題的一個誘人替代方案是分拆一個由嶄露頭角的 Rust 專家組成的小型子團隊,負責處理需要 Rust 效能或型別安全級別的關鍵任務元件。這是我們在 Magic Pocket 專案中犯的一個錯誤,少數工程師用 Rust 重寫了儲存引擎,而團隊的其他成員繼續用 Go 構建系統的其餘部分。當 Rust 元件是一個小型 skunkworks 專案時,這種拆分是必要的,但它在此後持續了多年。

Rust 元件是他們自己成功的犧牲品——它們執行良好,不需要太多維護,當最初的作者離開團隊去開發 Nucleus 時,已經沒有多少工程師留下來成為這些方面的專家——大型系統。這在一段時間內減緩了 Rust 元件的創新,並讓剩下的團隊對 Rust 的使用感到複雜。

當我們開始 Convex 時,我們發誓不再重複這個錯誤,並確保所有工程師都在 Rust 上工作並掌握該語言。

  • 作為組織負擔的庫包支援

有了 Nucleus(Dropbox 的下一個 Rust 主要專案),我們確保整個團隊都流暢並積極地為程式碼庫做出貢獻。在 Nucleus 團隊中,Rust 取得了明確的成功。桌面客戶端與後端程式碼庫相對隔離,開發團隊已經習慣了與多種語言的互操作。

在專案的邊界上,事情稍微複雜一些。大公司有數百個相互通訊的內部系統:監控、RPC 框架、釋出過程、身份驗證等。工程師需要將這些現有子系統移植到每種新的支援語言。Nucleus 的原型 API 伺服器 Tomahawk 最初是用 Rust 重寫的。Nucleus 團隊承擔了在 Tomahawk 內部實施內部系統支援的重擔。最終,移植整個生態系統的工作量太大,所以我們用 Go 重寫了 Tomahawk,Go 是公司事實上的後端語言。有時,移民的障礙實在是太高了。

在 Convex,我們可以從一開始就讓事情變得乾淨:所有後端程式碼都在 Rust 中;所有前端程式碼都在 Typescript 中;操作工具主要使用 Python。作為一家初創公司,我們需要專注於完成工作,而不是支援多種語言。

  • 遷移後

也許引入新語言的最大缺點之一不是它需要在新程式碼庫上做更多的工作,而是它減少了對現有程式碼庫的投資。我們將 Dropbox 的後端堆疊從 Python 切換到 Go,然後將一些備受矚目的專案從 Go 切換到 Rust。每次我們在效能、型別安全和生產力方面都獲得了很多。然而,隨著高階工程師轉向這些令人興奮的新專案,在現有程式碼庫上完成的前沿工作越來越少,因此對庫的持續投資也越來越少。

當一個團隊對另一個團隊提出功能請求時,他們自己介入並進行更改也很常見。通過簡單的程式碼審查而不是一系列 Jira 任務來協調更改通常要快得多,而且希望該功能的團隊投入時間來實現它是公平的。當團隊之間存在(程式設計)語言障礙時,這是一個重大挑戰。

初創公司不能在團隊之間建立人為的障礙。我們在 Convex 做出了有意識的努力,使程式碼庫對所有開發人員來說盡可能平易近人,並確保所有開發人員都擁有足夠的專業知識,可以在需要工作的任何地方工作。

 

引入Rust注意點

我們是 Rust 的大力倡導者,並從該語言中受益匪淺,但必須謹慎地將新語言引入組織。

對於新團隊

  • 首先決定你是否真的想使用 Rust。你會從型別安全和效能中受益嗎?驚人的。您是否主要是一個前端商店,其 IO 繫結的工作負載無論如何都會執行大致相同的工作?也許你最好使用像 Go、Java 或 Kotlin 這樣的垃圾收集語言。
  • 接受會有加速的時間。不管人們告訴你什麼,像 Rust 這樣的語言需要相當長的時間才能變得富有成效。確保你的團隊中已經有一些專家可以幫助其他人提升而不是同時放慢整個團隊的速度。當沒有現有水平的 Rust 專業知識作為指導時,我們已經看到專案進展不佳。
  • 決定您將擁有多少種語言,並負責長期支援它們。意識到以多種語言維護庫和作業系統的開銷。
  • 願意說不。有時,正確的解決方案就是湊合你已經擁有的東西。

對於現有團隊

  • 決定是否值得重寫或使用新語言。僅僅重構現有的程式碼庫就足夠了嗎?您是否確定了真正重要的熱點並嘗試優化它們?您對您想要進行的更改為客戶和開發人員帶來的好處有信心嗎?
  • 從一個不在關鍵開發路徑上的較小的子專案開始。這將使您能夠靈活地推遲時間表並適應意外的互操作性問題。
  • 一旦較小的專案取得成功,請確保整個團隊都在努力——您不希望專業知識分散在少數工程師之間。
  • 如果一切順利,承諾在整個公司更廣泛地傳播這種語言。
  • 承擔建立鄰近系統支援的個人責任。僅僅告訴監控團隊為您構建一套全新的客戶端繫結可能是不行的——您可能必須自己引導這項工作。
  • 確保核心系統的持續開發。核心系統需要一支工程師團隊,他們能夠在出現問題時進行創新或干預。即使沒有緊迫的功能需求,也總是有改進的地方。

 

相關文章