意外中斷與自動續跑
工作流程跑到一半被打斷怎麼辦?搭配 recovery 與 idempotent,跑斷點續傳、避免重複副作用。
"一次沒跑完怎麼辦"是生產級工作流必須回答的問題。Braidrun 提供兩種處理方案:
- 默認:安全中斷 — 把 execution 標為 INTERRUPTED,等你看清楚再手動從中間某一步"重試"。
- 可選:自動續跑 — 你顯式聲明"這條工作流可以續跑 + 哪些步驟冪等"後,平臺自動從中斷點繼續。
兩類:
- 平臺側 —— 服務重啟、實例被替換、宿主機故障。這些我們努力避免但不能 100%。
- 你側 —— 你主動"取消"某次 execution(停在 CANCELLED,不是 INTERRUPTED)。
只有前一類才會進入"自動續跑"流程,CANCELLED 的 execution 不會自動跑。
默認行為:安全中斷
發生平臺側中斷時,平臺:
- 把該 execution 狀態標為 INTERRUPTED;
- 保留所有已完成 step 的結果、產物、日誌;
- 你收到一條站內通知(也會按你的通知配置郵件 / Slack);
- 你去執行詳情頁,可以"從失敗那一步重試"—— 平臺複用前面 step 的結果。
這種默認對所有工作流都可用,不需要配置。適合:
- 工作流含對外副作用(發郵件 / 扣款 / 改數據庫)
- 生產還不確定"重啟續跑是否安全"時
可選:自動續跑
如果你的工作流是純數據管道(只讀 API + 生成報表),可以開啟自動續跑 —— 中斷髮生後平臺自動從中斷點繼續跑,不需要你手動點。
啟用的兩層 opt-in
要讓自動續跑生效,你需要在 YAML 裡兩層都聲明:
- workflow 級: 聲明
recovery.autoResumeOnRestart: true - step 級: 安全重跑的步驟標
idempotent: true
完整示例
name: daily-asa-digest
version: "1.0.0"
recovery:
autoResumeOnRestart: true
policy: RESUME_FROM_LAST_INCOMPLETE
maxAutoResumeAttempts: 3
steps:
- id: fetch_asa_data
type: sub_workflow
module: dingyue-module-asa-fetch
idempotent: true # 只读拉取,可重跑
- id: build_excel
type: sub_workflow
module: dingyue-module-excel-report
idempotent: true # 生成文件到产物目录,可重跑
- id: notify_team
type: sub_workflow
module: dingyue-module-slack-deliver
# 不写 idempotent —— 默认 false,发消息不可重跑三種 recovery.policy
| policy | 含義 | 適合場景 |
|---|---|---|
ABORT | 不自動恢復,等於回到默認行為 | 測試階段 |
RESUME_FROM_LAST_INCOMPLETE | 默認。從第一個非 COMPLETED 的 step 開始重跑。 | 數據管道、只讀 API 聚合、純計算 |
RESUME_FROM_START | 整條 workflow 從頭開始重跑 | 數據源可能在中斷期間變化、需要刷新的場景 |
idempotent 怎麼標
判斷一個 step 是否 idempotent 的黃金標準:
"同樣的輸入跑兩次,會不會產生副作用?"
如果會(對外發消息、扣款、寫訂單),就不要加 idempotent。
| Step 性質 | idempotent |
|---|---|
| 只讀 API 查詢(拉報表、列 App) | ✓ true |
| 純計算 / 數據轉換 | ✓ true |
| 生成 Markdown / Excel 到產物目錄 | ✓ true(重跑前舊產物會被清理) |
| LLM 推理(無副作用工具) | ✓ true(會重新計 tokens,但語義安全) |
| 發郵件 / Telegram / Slack / 飛書 | ✗ false |
| 寫入外部 DB / 改廣告出價 / 下單 / 扣款 | ✗ false |
| Webhook 出站調用 | ✗ false(除非對方 endpoint 明確去重) |
| manual_approval | ✗ false(續跑會再 ping 一次審批人,體驗差) |
不寫 idempotent 默認是 false。RESUME_FROM_LAST_INCOMPLETE 遇到非冪等 step 會降級為 ABORT —— 不自動跑,留給你手動。
自動續跑不會發生的幾種情況
即使你配好了 recovery,以下幾種 execution 仍然不會自動續跑:
- 觸發源是 Webhook —— Webhook payload 可能 at-most-once,平臺不知道上游是否會 retry。
- 觸發源是 Scheduler —— scheduler 會按 cron 下一輪自行 re-fire。
- 當前停在 manual_approval —— 等審批意味著本來就是阻塞狀態。
- workflow 定義文件在中斷期間被修改 —— 平臺檢測到 hash mismatch,不確定新版是否兼容。
手動"從某一步重試"(隨時可用)
不管工作流有沒有配 recovery,執行詳情頁始終提供"從失敗步驟重試"按鈕:
- 打開一條 INTERRUPTED / FAILED 的 execution;
- 點失敗 / 中斷的 step 旁的「從這一步重試」;
- 前面已 COMPLETED 的 step 結果會被複用;這一步開始往下重新跑。
即使這一步不是 idempotent,你也可以手動重試 —— 因為這時是你作為人有意識地決定"值得重跑"。
續跑限額
防止系統把同一條 execution 來回重啟"死循環":每條 execution 最多自動續跑 3 次。超過仍然失敗就停下來等你處理。你可以在 recovery.maxAutoResumeAttempts 裡覆蓋(1-20)。
審計
每次自動續跑都會記審計事件:
execution.auto_resume— 成功續跑execution.auto_resume.skipped— 按規則不應該續跑,帶 reason
Admin 可以在審計日誌裡按這兩個 event_type 過濾查看。
實戰建議
- 純數據管道(ASA 日報、RSS 摘要):recovery + idempotent 全開,安心讓它自動續跑。
- 含外部副作用的(扣款、發消息、下單):不要開 auto resume。中斷了人工決策。
- 關鍵副作用步驟前加 manual_approval —— 審批本身是中斷續跑的"天然阻塞點"。