平台與維運 2026 年 4 月 23 日

2026-04-23 — Cloudflare Rust Workers 實現 panic/abort 無損復原;Azure DevOps Git policy 查詢提速 15 倍

Making Rust Workers reliable: …

Making Rust Workers reliable: panic and abort recovery in wasm-bindgen

blog.cloudflare.com · 2026-04-22

Making Rust Workers reliable: panic and abort recovery in wasm-bindgen

Cloudflare Workers 的 Rust 支援已經走過一段不算短的路,但長期以來有個讓工程師頭痛的問題:只要 Worker 執行期間發生 panic,整個 Wasm 實例就會進入不可用的「毒化」狀態,後續的請求全部無法處理。這不只是 DX 問題,在 Durable Objects 等有狀態場景下,代價更是災難性的——實例的記憶體狀態會直接消失。這篇文章說明 Cloudflare 工程師如何與 wasm-bindgen 社群合作,一步步解決 panic 與 abort 的可靠性問題,最終在 workers-rs 0.8.0 落地。

第一階段:自定義 Panic Handler + Proxy 包裝(workers-rs 0.6)

最初的解法相對直接:在 Rust 側加入一個全域 panic handler,當 panic 被捕捉到時,把一個「已毒化」的 flag 設為 true,並在處理下一個請求之前先觸發完整的應用程式重新初始化(reinitialization)。JavaScript 側則透過 Proxy 包裝所有對外暴露的 entrypoint,確保無論哪個函數被呼叫,都能走過一致的錯誤邊界。

這個做法解決了「實例永遠掛掉」的問題,但代價是每次 panic 之後都得把整個 Wasm 模組重新初始化,對於 Durable Objects 這類需要在多次請求之間保持狀態的場景,等同於直接放棄所有記憶體中的資料。

第二階段:WebAssembly Exception Handling + panic=unwind

真正的突破來自 WebAssembly Exception Handling(WEH)規格在 2023 年獲得主流引擎的廣泛支援。WEH 讓 Wasm 模組可以拋出並捕捉帶有型別的 exception,這正是 Rust panic=unwind 語意所需要的基礎設施。

傳統上 Rust 編譯成 Wasm 時預設使用 panic=abort,因為 Wasm 沒有 unwinding 機制。有了 WEH,工程師可以改用 panic=unwind,讓 Rust 的 destructor(Drop trait)在 panic 傳播過程中如期執行,資源得以正確釋放,而 Wasm 實例的其餘狀態則繼續存活。

要讓這件事運作,團隊做了幾個底層修改:

  • Walrus WebAssembly parser:新增對 WEH 的 try/catch 指令的解析與處理支援。
  • wasm-bindgen descriptor interpreter:更新以理解新的 exception 型別描述符。
  • Rust export ABI:將跨越 Rust-JavaScript 邊界的 export 函數標記為 extern "C-unwind",允許 unwind 穿越 FFI 邊界。
  • JavaScript 側:panic 會以 PanicError 的形式浮現為 JavaScript exception,讓 JS 呼叫者可以正確捕捉並決定後續行為。

這樣一來,發生 panic 的那個請求會拋出 PanicError,但 Durable Object 的實例狀態完整保留,下一個請求可以繼續使用同一份記憶體,不需要重新初始化。

第三階段:Abort Recovery

Panic 解決了,但還有另一個更棘手的問題:abort。Abort 通常發生在 OOM(out-of-memory)或呼叫 std::process::abort() 的情況,它不走 unwind 路徑,而是直接終止執行。

Cloudflare 的解法是在 abort 路徑上也掛上 WEH 的 exception tag,用一個特殊的 tag 來區分「這是 abort」而非普通的 panic,讓 JavaScript 側可以識別並做不同的處置。此外,還新增了一個 set_on_abort hook,允許在 Worker 初始化時掛載自定義的 abort recovery handler,例如嘗試釋放快取、執行清理邏輯,或在記憶體不足時優雅降級。

函式庫模式:--reset-state-function

對於將 Rust Wasm 作為函式庫(而非完整 Worker)使用的場景,團隊還加入了一個實驗性的 --reset-state-function 機制。這讓 Wasm 模組可以暴露一個重置入口點,讓外部消費者(例如嵌入 Wasm 的 JS 應用)能夠在 abort 後重新初始化內部狀態,而不必丟棄整個 Wasm 實例並重新建立 binding。

生態系相容性

WEH 支援的工作不只在 Workers runtime 端,Cloudflare 也把相關補丁回移(backport)到 Node.js 22 和 Node.js 24 LTS,確保在標準 Node.js 環境中使用 panic=unwind 的 Rust Wasm 模組也能正常運作,擴大了整個 Rust + Wasm 生態系的受益範圍。

如何啟用?工程師可以這樣做

在 workers-rs 0.8.0 之後,只需在 Cargo.toml 設定 panic = "unwind" 並使用 --panic-unwind flag 編譯:

[profile.release]
panic = "unwind"

團隊計畫在後續版本中將 --panic-unwind 設為預設行為,屆時現有 Worker 不需任何改動即可受益。對於目前使用 Durable Objects 並在意狀態保留的工程師,現在就值得手動啟用測試。

結論

這個改進的技術意義遠超過 Cloudflare Workers 本身。它展示了 WebAssembly Exception Handling 規格在實際生產環境中的一個關鍵應用場景:讓 Rust 的 ownership 與 drop 語意在 Wasm 跨語言邊界的複雜環境中得以完整保留。對於任何在生產環境中跑 Rust Wasm 的團隊,無論是在 Workers、Node.js,還是其他 Wasm runtime,這套機制都是值得追蹤的方向。


Write azd hooks in Python, JavaScript, TypeScript, or .NET

devblogs.microsoft.com · 2026-04-22

Azure Developer CLI(azd)是 Microsoft 推出的雲端開發工作流程工具,讓工程師可以用一個指令完成從程式碼到 Azure 的完整部署流程。Hook 系統是 azd 的重要擴充機制——你可以在生命週期的關鍵節點插入自定義邏輯,例如在 provision 之前執行資料庫 migration、在 deploy 之後發送通知、或在任何階段做環境驗證。過去 hook 只支援 Bash 和 PowerShell 腳本,這對熟悉其他語言的工程師來說是個明顯的摩擦點。現在,azd 正式新增對 Python、JavaScript、TypeScript 和 .NET 的原生支援。

Hook 的基本概念

Hook 設定在 azure.yaml 中,可以掛在 provisiondeployupdownrestore 等生命週期事件的 prepost 階段。每個 hook 指定一個腳本路徑,azd 負責偵測語言並用對應的 runtime 執行,工程師不需要在設定中明確指定語言——副檔名就是線索。

hooks:
  preprovision:
    - script: ./scripts/validate-env.py
  postdeploy:
    - script: ./scripts/notify.ts

Python:自動虛擬環境與相依管理

azd 偵測到 .py 副檔名後,會在腳本目錄(或其父目錄)尋找 requirements.txtpyproject.toml。找到後自動建立虛擬環境(venv)、安裝相依套件,再執行腳本。整個流程不需要工程師手動 pip install 或啟用 venv,azd 代勞了。這對 CI/CD 環境特別友善,agent 上不需要預先準備好特定的 Python 環境。

JavaScript 與 TypeScript:tsx 加速開發體驗

JS/TS hook 需要腳本目錄下存在 package.jsonazd 支援 npm、pnpm、yarn 三種套件管理器,自動偵測並執行 install。TypeScript 腳本透過 npx tsx 執行,不需要預先編譯成 JS,這讓 TypeScript hook 的開發迭代速度與寫 Bash 一樣即時——改完存檔直接跑,沒有 build step 的摩擦。對於已經大量使用 TypeScript 的前端或全端工程師,這是一個很自然的延伸。

.NET:專案模式與單檔模式

.NET hook 有兩種執行模式:

  • 專案模式:腳本目錄下有 .csproj.fsproj.vbproj 專案檔,azd 執行 dotnet restoredotnet run
  • 單檔模式:需要 .NET 10 或以上版本,直接執行單一 .cs 檔,無需建立專案,類似 Python 腳本的輕量體驗。

對於組織內主力語言是 C# 的團隊,這讓 hook 腳本可以共用既有的 .NET 工具函式庫,不必另外維護一套 Bash 版本。

進階設定:dir 與平台差異化

所有語言的 hook 都支援 dir 欄位,用來覆蓋腳本的工作目錄,方便將腳本放在 monorepo 的任意位置。此外,hook 設定也支援平台差異化,可以針對 Windows(PowerShell/cmd)和 POSIX(Linux/macOS)指定不同的腳本,解決跨平台開發時的腳本相容問題:

hooks:
  preprovision:
    windows:
      - script: ./scripts/setup.ps1
    posix:
      - script: ./scripts/setup.py

對平台工程師的意義

這個更新的核心價值不只是「多支援幾種語言」,而是讓平台工程師可以用團隊最熟悉的語言來撰寫部署自動化邏輯,降低因為語言切換帶來的維護摩擦。尤其是 TypeScript 支援(透過 tsx 免編譯執行)和 Python 的自動 venv 管理,讓 hook 腳本的開發體驗向 application code 看齊,而不是退化到一個需要特殊知識的腳本語言孤島。

如果你的 azd 工作流程目前因為 hook 語言限制而有所妥協,現在值得重新評估,把那些勉強用 Bash 寫出來的複雜邏輯換成更易讀、易測試的 Python 或 TypeScript 實作。


Optimizing Git policy management at scale

devblogs.microsoft.com · 2026-04-22

當一個組織在 Azure Repos 上管理數十萬個 Git repository 時,如何有效率地執行 Git policy——確保每個 repo 都套用了正確的分支保護規則、推送限制——就成了一個基礎設施規模的問題,而不只是配置管理的問題。Microsoft 的 Azure DevOps 工程師 Azat Galiev 在這篇文章中說明他們如何透過 REST API 的改進,讓大規模 Git policy 管理的效能提升 10-15 倍,伺服器 CPU 使用量降低 50%。

問題背景:client-side filtering 的規模陷阱

Azure Repos 的 Git policy 系統允許管理員為 repository 設定各種規則:必須通過 CI build、需要 reviewer 核准、禁止直接 push 到特定分支等。這些 policy 可以套用在整個 repo 層級(push policy),也可以套用在特定分支(branch policy),而且支援透過 glob pattern(例如 releases/*)讓一條規則覆蓋多個分支。

原本的 REST API 設計中,如果你想知道某個特定 repo 的所有有效 policy,你必須:

  • 呼叫 GET /_apis/git/policy/configurations,不帶 repo 過濾,取得整個 project 的所有 policy 設定。
  • 在 client 端過濾,找出哪些 policy 的 scope 包含你的 repo。

在小型組織這無關痛癢,但在擁有幾十萬個 repo 的企業環境,單次查詢的回應可能高達數百 MB。更糟的是,自動化的 policy 治理工具需要定期對所有 repo 執行這個查詢,每天累積的執行時間就是 1,000 到 3,000 小時。

解法:server-side filtering 與 refName=~all

改進的核心是在 GET /_apis/git/policy/configurations 端點新增對一個特殊值 ~all 的支援。當你同時帶上 repositoryIdrefName=~all 時,API 會在伺服器端完成所有過濾,只回傳與指定 repo 相關的 policy:

GET https://dev.azure.com/{org}/{project}/_apis/git/policy/configurations
    ?repositoryId={repoId}
    &refName=~all
    &api-version=7.2-preview.1

這個端點的回應包含兩類 policy:

  • Push policy:套用在整個 repo 層級的規則(scope 包含 repo ID 但不指定 ref)。
  • Branch policy:套用在一或多個分支的規則,包括透過 glob pattern(如 releases/*)繼承(inherit)的 policy。

特別值得注意的是「繼承」的處理:過去這是 client-side filtering 最難正確實作的部分,因為你需要自己解析 glob pattern 並判斷是否匹配。新的 API 在伺服器端處理繼承邏輯,確保回傳結果的語意是正確的,不會有遺漏或誤判。

效能數字

Microsoft 內部的測量結果:

  • 伺服器端 CPU 消耗降低 50%
  • 自動化 policy 治理工具的每日總執行時間從 1,000–3,000 小時降至 100–150 小時,約 10–15 倍的改善。

這個數字的規模感值得停下來想一下:50% 的 CPU 節省在一個規模如此大的服務上,代表的是真實的硬體成本;而執行時間從數千小時降到百小時,對於依賴這些工具的 platform team 來說,也意味著更快的 feedback loop 和更低的 CI 排隊時間。

對 platform engineer 的實際影響

如果你負責維護一個大型組織的 Azure DevOps policy 合規工具,這個 API 改動直接影響你的工具架構。舊的模式是「拉全量,本地過濾」,新的模式是「精確查詢,按需取用」。

遷移的方式很簡單:在原本的 API 呼叫中加上 refName=~all 參數,移除 client-side 的 policy scope 過濾邏輯。如果你的工具需要處理繼承 policy,不需要自己實作 glob matching,直接信任 API 回傳的結果即可。

這也是一個很好的設計案例:把「知道哪些 policy 有效」這個領域知識從 client 移回 server,不只提升效能,也提升了正確性——因為伺服器比任何 client 都更了解自己的資料模型,尤其是涉及繼承和 glob 匹配這類隱含語意的場景。

結論

這是一個典型的「找到正確的 API 設計就能消除大量無效工作」的案例。refName=~all 是個很小的介面改動,但背後代表的是把 O(全量 policy 數) 的查詢轉換成 O(單一 repo 相關 policy 數),在規模化的環境下這個差異是決定性的。對於任何在 Azure DevOps 上維護 policy-as-code 或合規自動化的工程師,這個 API 版本(api-version=7.2-preview.1)值得立即評估是否納入工具鏈。


來源:blog.cloudflare.com, devblogs.microsoft.com

End of article
0
Would love your thoughts, please comment.x
()
x