Skip to content

中止 / 重試 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 detectedRetry;多半是 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.skipSkip task(含理由)
bb.taskrun.cancelCancel 執行中的 TaskRun
bb.rollout.pause / bb.rollout.resume / bb.rollout.cancelRollout-level 操作

事後追溯:查詢 audit log

不要做的事

反模式為什麼
Retry 前不看 DB 狀態對非 idempotent SQL = 二次施加
Cancel 後假設 DB 沒改DB 端可能 partial-applied
為了趕進度 skip 一堆 task各 task 對應 revision,skip 會造成版本漂移
在 prod rollout 一半遇問題就「再跑一次看看」不是 transient 錯誤的話只是放大事故

相關

Argus — 公司內部資料庫變更審計平台