Appearance
中止 / 重試 task
何時用這份文件 Rollout 執行過程中某個 TaskRun 失敗、卡住、或產出非預期結果,你要決定下一步。
前置條件
- 你擁有
bb.taskRuns.cancel(多半是workspaceDBA/projectOwner) - 你有目標 DB 的直連權限(用來檢視真實狀態)
決策樹
text
TaskRun 失敗或卡住
│
▼
┌──────────────────────────────────┐
│ 1. 看清楚現在 DB 上的真實狀態 │
└──────────────────────────────────┘
│
▼
┌──────────────────────────────────┐
│ 2. 失敗原因是什麼? │
└──────────────────────────────────┘
│
├─ Transient(網路、deadlock、暫時鎖等待) → Retry
├─ Idempotent SQL(重跑同段 SQL 安全) → Retry
├─ 非 idempotent(INSERT VALUES、ALTER 已半成) → 看 DB 狀態決定 Skip / 修補 / Abort
├─ SQL 邏輯錯(語法 / 業務錯誤) → Abort,開新 Plan
├─ 環境問題(權限、磁碟滿、replication 斷) → 修環境後 Retry
└─ Prod 已執行造成業務影響 → 走 [緊急回滾](/operations/runbooks/emergency-rollback)動作清單
Retry
bash
# UI
Rollouts → <id> → Task <n> → 點失敗的 TaskRun → Retry
# API
curl -X POST -H "Authorization: Bearer $TOKEN" \
"$ARGUS/v1/projects/<p>/rollouts/<r>/tasks/<t>/taskRuns:run"Argus 行為:
- 凍結舊 TaskRun 為
failed(保留所有 log) - 建立新 TaskRun,
attempt = old + 1 - 重跑 同樣 的
executed_sql
⚠️ Retry 不會自動跳過 partial-applied 的 SQL。如果上一次跑到一半 commit 了一些 statement,retry 從第一個 statement開始重跑。
→ 對非 idempotent SQL,retry 前 務必確認 DB 狀態。
Skip
bash
# UI
Task → 點 ⋯ → Skip Task
# API
curl -X POST -H "Authorization: Bearer $TOKEN" \
"$ARGUS/v1/projects/<p>/rollouts/<r>/tasks/<t>:skip" \
-d '{"reason":"Manually applied in DB on 2026-05-24 by alice."}'用在:
- 你已手動在目標 DB 完成了該變更
- 該 task 對應的變更已被其他 task 涵蓋(重複)
- 該 task 因業務原因不再需要
⚠️ Skip 寫進 audit log(bb.task.skip,含理由)。但不會寫 revision — 如果你手動執行了該變更,要自己 補登 revision。
Cancel
bash
# UI
Task → 點 ⋯ → Cancel
# API
curl -X POST -H "Authorization: Bearer $TOKEN" \
"$ARGUS/v1/projects/<p>/rollouts/<r>/tasks/<t>:cancel"對「執行中」的 TaskRun:
- Argus 嘗試中止 DB 端執行(送
pg_cancel_backend()或同等指令) - 不保證 DB 端真的停下來(若已進 commit 階段,不可中止)
- TaskRun 狀態變
canceled,但 DB 端可能仍 partial-applied
⚠️ Cancel 後一定要直連 DB 確認真實狀態。不可假設「Argus 顯示 canceled = DB 沒改」。
Pause Rollout
bash
# UI
Rollouts → <id> → Pause
# API
curl -X POST -H "Authorization: Bearer $TOKEN" \
"$ARGUS/v1/projects/<p>/rollouts/<r>:pause"讓整個 Rollout 不再啟動新 task。已執行中的 task 不會被中止;要停那個 task 用 Cancel。
Pause 適合:「我要先停下來看清楚現況」。
Abort Rollout
bash
# UI
Rollouts → <id> → Cancel Rollout整個 Rollout 進入 canceled 狀態,所有 pending task 不會再執行。已執行的 task 不會 自動回滾。
真正需要回滾 = 走 緊急回滾。
場景對照
場景 1:deadlock 失敗
| 訊號 | 處理 |
|---|---|
| TaskRun fail with deadlock detected | Retry;多半是 transient |
場景 2:磁碟滿了
| 訊號 | 處理 |
|---|---|
| TaskRun fail with disk full / no space left | 先擴容,再 Retry |
場景 3:CREATE INDEX 跑了 30 分鐘還沒結束
| 訊號 | 處理 |
|---|---|
TaskRun running,無 error,但 DB 端 pg_stat_activity 顯示是 building index | 等。CREATE INDEX CONCURRENTLY 大表需要時間 |
| 同上但 DB 端已停止活動 | Cancel,看 DB 端狀態(可能有殘留 INVALID index) |
場景 4:ALTER TABLE 在 commit 階段失敗
| 訊號 | 處理 |
|---|---|
| TaskRun fail,error 出現在 commit 之後 | 連 DB 看 ALTER 是否已套用 |
| 已套用 | Skip 該 task,補登 revision |
| 沒套用 | Retry |
場景 5:跑 prod 後業務出狀況
| 訊號 | 處理 |
|---|---|
| TaskRun done,但業務指標惡化 | 不要 嘗試 retry / cancel;走 緊急回滾 |
直連 DB 檢查清單
當你需要看 DB 真實狀態(PG 範例):
sql
-- 表結構
\d+ <table>
-- 當前活動
SELECT pid, state, wait_event, query
FROM pg_stat_activity
WHERE datname = '<db>'
AND state != 'idle'
ORDER BY query_start;
-- 鎖
SELECT * FROM pg_locks WHERE NOT granted;
-- 我關心的 index 是否存在 / valid
SELECT indexname, indexdef, indisvalid
FROM pg_indexes JOIN pg_index ON pg_indexes.indexname = pg_index.indexrelid::regclass::text
WHERE schemaname = 'public';
-- 我關心的 column 是否存在
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = '<table>';「Argus 顯示什麼」與「DB 上實際有什麼」可能不同。永遠以 DB 為準。
Audit log 寫的動作
| 動作 | 觸發 |
|---|---|
bb.taskrun.start | 觸發 retry |
bb.taskrun.fail | 自動,於 TaskRun 失敗時 |
bb.task.skip | Skip task(含理由) |
bb.taskrun.cancel | Cancel 執行中的 TaskRun |
bb.rollout.pause / bb.rollout.resume / bb.rollout.cancel | Rollout-level 操作 |
事後追溯:查詢 audit log。
不要做的事
| 反模式 | 為什麼 |
|---|---|
| Retry 前不看 DB 狀態 | 對非 idempotent SQL = 二次施加 |
| Cancel 後假設 DB 沒改 | DB 端可能 partial-applied |
| 為了趕進度 skip 一堆 task | 各 task 對應 revision,skip 會造成版本漂移 |
| 在 prod rollout 一半遇問題就「再跑一次看看」 | 不是 transient 錯誤的話只是放大事故 |