Cloudflare Workers 啟動太慢怎麼查?用 Wrangler check startup 找出 bundle 與全域初始化
Cloudflare Workers 部署出現 Script startup exceeded CPU time limit 時,先不要只加大 request 的 CPU limit。這個錯誤代表 Worker 在進入 handler 前執行全域程式碼超過平台的 startup 限制;Cloudflare 的錯誤碼是 10021,目前 startup time 上限為 1 秒。
直接答案是:先用 wrangler check startup 取得 bundle 大小與本機 CPU profile,再用 wrangler deploy --outdir ... --dry-run 對照壓縮後大小,最後針對 profile 顯示的全域 import、schema 解析或初始化邏輯修改。 本機時間不能直接當成 Cloudflare 生產環境時間,但能幫你找到啟動階段做了什麼。
先理解 startup 在量什麼
Worker 的 global scope 是 handler 之外、模組載入時就會執行的程式碼,例如頂層 import 的初始化、建立大型 schema、解析設定檔或建立昂貴的資料結構。Cloudflare 要求這段 parse and execute 在 1 秒內完成;超過時部署可能回報 10021。
這和請求進入 fetch() 後執行太久是兩個問題。把 handler 的 request CPU limit 調高,不能修復 global scope 的 startup 超時;先看錯誤發生在部署驗證、啟動階段還是單一請求。
第一步:讓 Wrangler 產生 profile
在使用專案實際 Wrangler 版本的前提下執行:
pnpm exec wrangler check startup命令會輸出 bundle 與 gzip 大小,並在專案中產生 .cpuprofile。把檔案匯入 Chrome DevTools 的 Performance/CPU profile,或直接用 VS Code 開啟,觀察 flamegraph 哪些函式佔用啟動樣本。
如果正式部署使用額外 bundling 參數,profile 必須使用同一組參數。例如部署指令包含 --no-bundle,就用:
pnpm exec wrangler check startup --args="--no-bundle"不使用 Wrangler 直接部署時,改用 --worker 提供符合 Cloudflare API 格式的 bundle;Pages 專案若設定檔沒有 pages_build_output_dir,再考慮 --pages。這些選項與限制請以 Wrangler workers command reference 為準。
第二步:檢查 bundle 大小,不要只看 profile
Cloudflare 目前列出的壓縮後 Worker 大小限制是 Free 3 MB、Paid 10 MB,未壓縮上限都是 64 MB;較大的 bundle 可能增加 startup time。用 dry run 檢查實際上傳輸出:
pnpm exec wrangler deploy --outdir bundled/ --dry-run這個結果比 src/ 目錄大小有意義,因為 Worker 會經過 bundling、tree-shaking 和壓縮。若 bundle 接近限制或 profile 顯示某個大型套件在 global scope 初始化,先移除不需要的依賴、縮小匯入範圍,或把靜態資料移到 KV、R2、D1 或 Workers Static Assets;需要拆分職責時再考慮 service bindings。
不要把「bundle 小」當成 startup 一定安全。小 bundle 仍可能在頂層解析大型 schema、掃描資料或建立大量物件;反過來,某些較大的模組只載入常數,也不一定是 profile 的主要熱點。
第三步:按 profile 的熱點修正
先把 flamegraph 中的工作分成三類:
| 類型 | 常見線索 | 修正方向 |
|---|---|---|
| 不必要的依賴 | 某套件整包出現在 import 路徑 | 改用精確匯入、移除不使用的套件 |
| 頂層 CPU 工作 | schema、正規表示式表或資料轉換在模組載入時執行 | 移到 build time 或 handler 內,依請求需求快取結果 |
| 大型靜態資料 | JSON、二進位或 prompt 直接打包進 Worker | 放到合適的 Cloudflare 儲存服務,執行時讀取 |
「移到 handler」不是萬用解法:每次請求都重做昂貴初始化,可能只是把 startup 問題換成 request latency。若資料不會變,優先在 build time 產生精簡結果;若必須在執行時載入,確認快取生命週期與失敗路徑。
如果功能邊界本身已經很大,拆成多個 Worker,再用 service binding 分開部署與觀察;不要為了通過大小檢查,把無關程式碼藏進動態 import 而失去可測試性。
第四步:用部署輸出確認結果
修改後至少重跑三個檢查:
pnpm exec wrangler check startuppnpm exec wrangler deploy --outdir bundled/ --dry-runpnpm exec wrangler deploy部署輸出會提供 startup_time_ms;但本機 profile 和 Cloudflare 的數字要分開記錄。官方文件明確提醒,本機硬體與 Cloudflare 執行環境不同,兩者的 duration 可以有很大差異。你要追蹤的是:同一部署建置下,profile 熱點是否消失、bundle 是否下降,以及實際部署是否仍通過 startup validation。
若使用 CI 部署,將 check startup 放在與正式部署相同的建置參數後面,並把 .cpuprofile 作為除錯 artifact 保存;不要只保存一個「失敗」字串,否則下一次回歸還要重新猜測熱點。
這個問題也適合放進測試與部署檢查流程;若要確認修改後的 Worker 行為而不是只有 bundle,接著看 Workers integration test harness 的回歸流程。
常見問題
Q: wrangler check startup 的本機時間就是正式 startup time 嗎?
A: 不是。它在你的電腦上量測並產生 CPU profile,硬體與 Cloudflare runtime 不同,因此只能用來找初始化熱點。正式部署輸出中的 startup_time_ms 和平台驗證結果才是生產環境檢查。
Q: 10021 是單一請求 CPU 超時嗎?
A: 不是。10021 對應 Script startup exceeded CPU time limit,指的是 global scope 在 Worker 啟動時超過限制。單一請求的 CPU 問題要看另外的 request 執行與 CPU 指標。
Q: 只要把程式碼搬進 fetch() 就能修好嗎?
A: 不一定。這會避開啟動階段,但可能讓每次請求重新做昂貴工作。先依 profile 判斷是否能移到 build time、快取初始化結果、縮小依賴或拆分 Worker,再選擇搬移位置。
參考資料:
回報錯字、失效連結,或告訴我你想看的延伸主題。