Reconciliation 與 data repair 的核心責任是把資料錯誤從模糊異常轉成可驗證、可修復、可稽核的流程。進入特定資料庫或 ORM 前、讀者需要先理解資料修復屬於正式狀態責任的一部分。

本章涵蓋對帳作業的設計、不一致按成因的分類與偵測方式、修復的策略與執行控管,以及 audit、權限與 backup recovery 怎麼接進修復流程。

Reconciliation

Reconciliation 的責任是比較兩個或多個資料來源、確認正式狀態是否與外部事實一致。付款狀態要和金流 provider 對齊、發票狀態要和開票系統對齊、庫存狀態要和出貨或倉儲系統對齊。

對帳需要明確定義資料來源、時間窗、比對鍵、差異分類與 owner。這些欄位能把「資料看起來不一致」轉成可分派、可修復、可驗證的決策材料。

對帳系統的設計欄位

設計對帳作業時、要先把這幾件事談清楚、再寫 query。少談任何一項、對帳結果都會在事故當下被質疑可信度。

內部紀錄與外部事實:對帳的兩個來源,一個是內部的 source of truth(本章稱內部紀錄)、一個是外部事實。金流對帳的內部紀錄是訂單表、外部事實是 provider 結算檔;庫存對帳的內部紀錄是訂單庫存表、外部事實是倉儲 WMS 報表。兩個來源都要有明確 owner、否則差異發生時沒人能解釋為何資料長那樣。

比對鍵(comparison key):內部紀錄跟外部事實要用什麼欄位對齊。最理想是雙方共用的業務 ID(例如金流交易序號);次優是 timestamp + 業務外鍵組合;最差是用 fuzzy matching(金額 + 時間範圍)、這時對帳結果天然帶有噪音、要在 output schema 標示信心度。

時間窗(time window):對帳要對哪段時間的資料、什麼時候做。每日對帳通常設定 T-1 整天、跳過今天(避免 in-flight 資料);分鐘級對帳要明確處理 in-flight:是排除最近 N 分鐘、還是允許重複跑直到收斂。在跨時區業務裡、時間窗要對齊雙方 timezone、不然每天差異會穩定出現在 0:00 前後。

差異分類規則:mismatch 不是只有「不一致」一種。常見要再切:「內部紀錄有、外部事實沒有」(missing externally)、「外部事實有、內部紀錄沒有」(missing internally)、「兩個來源都有但欄位不同」(value mismatch)、「同一個比對鍵在內部紀錄有多筆」(duplicate)。每類差異的處理路徑跟 owner 都不同、不分類會讓修復決策無法分派。

Output schema:對帳產出的不是「對 / 不對」、而是一份結構化報告。最少要有:mismatch 樣本(不是全部)、總筆數與金額影響、覆蓋率(總共比對了多少筆)、未覆蓋資料(內部紀錄或外部事實裡有哪些資料沒被這次比對涵蓋)、結果時間戳。這份報告會被 4.20 Observability Evidence Package 收進釋出證據鏈、結構不穩定會讓上游 release gate 拒絕採信。

對帳跟 anomaly detection 的差異

兩件事都是「找資料異常」、但本質不同、不能互相替代。

對帳是 deterministic:給定兩個來源、結果是確定的差異集合、可以被任何工程師重跑驗證。anomaly detection 是 statistical:用模型或閾值判斷一筆資料是否「看起來不對」、結果帶機率、不同模型跑出來不一樣。

在金流、庫存、付款這類正式狀態場景、對帳是必須、anomaly detection 是補充。anomaly detection 適合抓「對帳沒設計到的維度」(突然某 tenant 訂單量爆增)、但不能用它當 source of truth、因為事故時無法回答「為何這筆被判定為異常」。

兩者輸出格式也不同:對帳輸出 mismatch list、anomaly detection 輸出 confidence score。把兩者混在同一份報告會讓 incident reviewer 無法判斷哪些是必修、哪些是可疑。

不一致按成因分類:時間性、結構性、語意

不是所有「資料不一致」都一樣。按 成因 分三類、各有不同處理策略。

Temporal Inconsistency(時間性不一致)

  • 成因:replication lag、async event delivery、eventual consistency
  • 特徵:互相比對的兩份資料都是「對的」、只是反映的 時間點 不同
  • 例:cache 跟 DB 看到不同 value(cache 還沒 invalidate)、replica 跟 primary 不同步
  • 處理:等待收斂或主動觸發 sync、不必修資料
  • 持續時間:通常 < 1 秒到分鐘級

Structural Inconsistency(結構性不一致)

  • 成因:schema migration 期間、dual-write 失敗、partial write
  • 特徵:互相比對的兩份資料應該一致但實際不一致、其中一份是 錯的
  • 例:訂單寫進主表但 line items 沒寫、外鍵 reference 一個不存在的 row
  • 處理:必須修復、不能等
  • 持續時間:永久(直到修復)

Semantic Inconsistency(語意不一致)

  • 成因:業務邏輯 bug、應用層 race condition、人工誤操作
  • 特徵:資料結構 OK、但 業務語意 錯
  • 例:訂單付款狀態是 paid 但金流端是 refunded、帳戶餘額跟交易紀錄 sum 不符
  • 處理:複雜、需要業務判斷哪邊是 source of truth
  • 持續時間:永久(且容易擴大)

處理優先序:Semantic > Structural > Temporal。Semantic 影響業務最深、Temporal 通常自動收斂。

偵測模式

不同類型的不一致需要不同偵測模式。

Continuous Detection(持續偵測)

  • 每筆寫入跑 sanity check(trigger、constraint)
  • 應用層 invariant check
  • 適合:structural inconsistency(讓 DB 自己擋)
  • 成本:每筆寫入有 overhead

Scheduled Detection(定期對帳)

  • 每 N 分鐘 / 每天跑對帳 query
  • 跟外部 provider 比對
  • 適合:semantic inconsistency(業務級對齊)
  • 成本:對帳 query 本身耗資源

Sampling Detection(抽樣偵測)

  • 不跑全表、抽樣 10% / 1% 跑 checksum
  • 適合:大表(全表對帳成本高)
  • 成本:可能漏掉低頻 inconsistency

Reactive Detection(反應式偵測)

  • 用戶 / 客服回報後才查
  • 適合:長尾(long-tail)inconsistency(找不到通用 pattern)
  • 成本:用戶體驗已受影響

對應 Zomato — migration 期間 shadow read 持續對帳、抓 mapping 規則漂移。

Data Repair

Data repair 的責任是把已確認的資料差異修回正式狀態、並保留修復原因、範圍、證據與回退條件。修復可以是 SQL update、補事件、補發 webhook、重建 projection 或人工客服流程、但每種修復都要有範圍控制。

資料修復要先分成三種:

類型說明常見風險
欄位修復修正單筆或小批正式欄位mapping 規則錯誤會造成二次污染
派生狀態重建重建 index、cache、read model可能掩蓋正式狀態尚未修復
補償動作補退款、補發票、補通知可能產生重複副作用

修復前要先確認錯誤落在哪裡:正式欄位錯誤要修 source of truth;派生狀態錯誤要重建副本;外部副作用漏做要走補償流程。

欄位修復的判讀重點是 mapping 規則是否正確、因為錯誤規則會把單點差異擴成批次污染。派生狀態重建的判讀重點是 source of truth 是否已經正確、否則重建會複製錯誤。補償動作的判讀重點是副作用是否可逆、因為退款、通知或外部 webhook 可能已經被使用者或第三方看見。

Repair 原則

不管哪種修復、都遵守三個原則:

Idempotency(冪等)

  • 同樣的修復跑兩次、結果跟跑一次一樣
  • 修復寫成「設成目標值」、不寫成「在現值上加減」,再加上「現值還不是目標值」的條件,重跑時就不會再碰到已經修好的列(下方區塊以 SQLite 實際跑過,accounts 兩列的 balance 起始都是 500,另掛一個 AFTER UPDATE trigger 把每次更新寫進 audit_log)
1-- 在現值上加減:跑兩次就補兩次
2UPDATE accounts SET balance = balance + 100 WHERE id = 1;   -- 跑兩次:500 → 600 → 700
3
4-- 寫成目標值:跑幾次都停在 600,但每跑一次都算改到 1 列,audit trigger 也每次都寫一筆
5UPDATE accounts SET balance = 600 WHERE id = 2;
6
7-- 目標值加上「現值還不是目標值」的條件:已經修好的列不再符合,改到 0 列,trigger 不觸發
8UPDATE accounts SET balance = 600 WHERE id = 2 AND balance <> 600;

Auditable(可稽核)

  • 每次修復都有 record:誰、什麼時候、改了什麼、為什麼
  • 修復前 + 修復後的 snapshot 都要存
  • 對應 Audit Log 卡片、1.5 Red Team 的 audit 段

Reversible(可逆)

  • 萬一修復是錯的、能回退到 before state
  • 不可逆操作(DELETE)必須有 dry-run、必須備份
  • 對應 Rollback Window 卡片

修復前的 dry-run 與 impact assessment

修復前要先回答「這次修復會碰多少筆、影響多少業務、最壞情況是什麼」、才能進入執行。直接跑 update 是 production-grade 流程的反例、即使在 incident 壓力下也不能跳過這步。

Dry-run 的責任:用正式修復的同一段 WHERE 條件先跑 SELECT、產出將被修改的資料樣本,只讀不寫。Dry-run 結果要包含:影響筆數總計、影響金額或業務值(如果有)、affected tenant / user list 的抽樣、未涵蓋的邊界 case。Dry-run 跟正式修復必須共用 mapping 規則、否則 dry-run 結果無法當審核依據。下面以 PostgreSQL 16 實際跑過,修的是「provider 已經結算成功、訂單卻還停在 pending」的差異:

 1-- dry-run:影響筆數與影響金額
 2SELECT count(*) AS affected_rows, sum(amount) AS affected_amount
 3FROM orders
 4WHERE status = 'pending'
 5  AND payment_id IN (SELECT payment_id FROM provider_settlements WHERE result = 'captured');
 6-- 2 | 500
 7
 8-- dry-run:給審核者看的樣本
 9SELECT tenant_id, id, status, payment_id, amount
10FROM orders
11WHERE status = 'pending'
12  AND payment_id IN (SELECT payment_id FROM provider_settlements WHERE result = 'captured')
13ORDER BY tenant_id, id
14LIMIT 20;
15
16-- 正式修復:WHERE 條件與 dry-run 逐字相同,改到的列數與 dry-run 的筆數一致
17UPDATE orders SET status = 'paid'
18WHERE status = 'pending'
19  AND payment_id IN (SELECT payment_id FROM provider_settlements WHERE result = 'captured');
20-- UPDATE 2

規模分級的執行策略:影響筆數會決定執行方式。

  • 單筆到十筆:客服等級的修復、一名工程師執行 + 一名同儕審核 + audit log 即可。
  • 百筆到千筆:要在低流量時段執行、分批跑、每批跑完比對 invariant、發現意外停下。
  • 萬筆以上:當成 production deploy 處理、要有 deploy review、staged rollout(先 1% tenant、再 10%、再全量)、跟 oncall 同步。
  • 跨表 / 跨 service:必須先做跨團隊 review、確認下游依賴(cache、search index、外部 webhook)的處理計畫、不能單一團隊獨自決定。

Impact assessment 的必看欄位:除了筆數、還要看 連帶影響。修復 orders 表會不會觸發 audit trigger 把每筆寫進 audit log 表?會不會觸發 outbox event 把每筆當成新事件對外發布?會不會讓某 tenant 的 metric 一次性異常、誤觸 alert?這些 second-order effect 在 dry-run 階段就要識別、否則修復本身會變成新事故。

Sandbox / staging 驗證:不可逆或大規模修復、先在 staging 跑一次、確認 query plan、執行時間、lock 行為。Production 規模沒辦法在 staging 重現的話、至少要在 production 的某個低風險 tenant / region 先試跑、再擴大。

Approval gate(4-eyes process):超出單筆規模或修復金錢、權限、個資的場合、必須 兩位以上人員 各自看過 dry-run 結果再簽核。常見實作是:執行者提 PR / ticket 帶 dry-run output、reviewer 簽核後才能執行、執行後產出 audit log 帶兩人簽核紀錄。Reviewer 的責任是獨立驗證 dry-run 結果跟 incident 描述一致,而不是當橡皮圖章。

Repair Patterns

實務上常見的 repair pattern:

條件式 UPDATE

最簡單也最安全的修復。

1UPDATE orders
2SET status = 'paid'
3WHERE id = 12345
4  AND status = 'pending'
5  AND payment_id = 'abc';

AND 條件確保只在 當前狀態符合預期 時才改、避免 race condition。

批次修復 + 節流

大量資料修復、必須節流避免影響 production:每批只改固定筆數,批與批之間停一段時間,直到沒有列可改。下面的迴圈以 PostgreSQL 16 實際跑過,263 筆待修的列分成 100、100、63 三批修完,第四次查不到可改的列而結束:

 1# 連線資訊由 PGHOST、PGUSER、PGDATABASE 等環境變數提供
 2while true; do
 3  n=$(psql -tA -c "
 4    WITH batch AS (
 5      SELECT id FROM orders WHERE status = 'broken' ORDER BY id LIMIT 100   -- 每批 100 筆
 6    ), fixed AS (
 7      UPDATE orders SET status = 'fixed'
 8      WHERE id IN (SELECT id FROM batch)
 9      RETURNING 1
10    )
11    SELECT count(*) FROM fixed;")
12  echo "本批修了 $n 筆"
13  [ "$n" -eq 0 ] && break    # 沒有列可改就結束
14  sleep 1                    # 批與批之間停 1 秒
15done

MySQL 不接受在 IN 子查詢裡寫 LIMIT(MySQL 8.4 回 ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'),單表的批次改用 UPDATE 自帶的 LIMIT,迴圈的結束條件看 ROW_COUNT():

1UPDATE orders SET status = 'fixed' WHERE status = 'broken' ORDER BY id LIMIT 100;
2SELECT ROW_COUNT();   -- 本批改到的列數,0 代表修完

對應 Backfill 卡片 — backfill 跟 batch repair 是同類技術。

補事件 / 補 webhook

外部副作用漏做時、補發事件。

  • 必須帶 idempotency key(third-party 才能去重)
  • 紀錄補發原因(incident report 連結)
  • 注意:補發前確認 third-party 是否真的沒收到

重建 derived state

cache 跟 search index 是 derived state、出錯通常 砍掉重建。

  • invalidate 讓下次 read 重算,而不是直接修 cache value
  • 大規模重建用 batch job 跑、避免 thundering herd
  • 對應 Tubi feature store 重建模式

從備份回復(Point-in-time Recovery)

當資料 損毀且無法重建 時、靠 backup recovery。

  • PostgreSQL:WAL + base backup → PITR
  • MySQL:binlog + snapshot → PITR
  • Aurora:cluster snapshot + continuous backup
  • 注意:recovery 期間可能要 整個 DB restore、影響範圍大

Repair Runbook

Repair runbook 的責任是讓資料修復可重複執行、並降低對當下工程師記憶的依賴。最小 runbook 需要包含:

  1. 差異查詢與 query link
  2. 影響範圍與 tenant / region / time range
  3. 修復方式與 dry-run 結果
  4. 審核 owner 與執行 owner
  5. rollback condition 與後續 validation query

runbook 要和 validation query 共用語意。若查詢與修復程式用不同 mapping 規則、修復結果就難以被同一份 evidence 驗證。

Audit 與權限邊界

Data repair 常常需要高權限、因此必須接到 audit 與資料保護邊界。修復個資、付款、權限或方案資料時、要保留操作者、審核者、查詢範圍、寫入範圍與修復前後樣本。

Audit log 必要欄位:

  • timestamp(操作時間)
  • actor(誰執行)
  • reviewer(誰審核、如果是 4-eyes process)
  • query(執行了什麼 SQL / API call)
  • before / after snapshot(值的變化)
  • reason(為什麼做這次修復、incident ID)
  • rollback path(如何回退)

這裡要接到 7.7 Audit Trail 與 Accountability Boundary。資料修復同時是可靠性、資安與合規問題。

權限分離與存取憑證時效

修復權限不該是常駐權限。日常開發 / SRE 帳號只該有 read-only、修復需要時才透過 break-glass 流程申請臨時 write 權限。

常見實作:

  • 角色分離:reviewer 跟 executor 是不同帳號、reviewer 不能執行、executor 不能 self-approve。系統強制檢查兩個帳號不同、避免一人偽造另一身分。
  • 時效性存取憑證:申請 write 權限時帶 expiry(30 分鐘 / 2 小時)、過期自動回收。不是「給了就一直有」、避免遺留高權限帳號變成攻擊面。
  • 範圍限定:申請時要指定哪張表、哪個 tenant / region。粒度不細的話、一次申請就拿到全 production write、超出實際需求。
  • 同步 alert:高權限被啟用要同步發 alert 到 security channel、給 security team reviewer 看見。事後若 audit log 跟 alert 對不上、表示權限被繞過。

對應 Identity Access Boundary 跟 Secrets and Machine Credential Governance。修復權限管理跟 incident-time 緊急存取是同一套機制、不該各做各的。

跨服務 / 跨組織的對帳責任

當對帳跨團隊、跨子系統、跨外部 provider 時、責任不清是首要失敗模式。對帳結果在組織邊界 穿越 時、要明確標記每段的 owner、否則 mismatch 出現後、所有相關方都會說「不是我們的問題」。

跨服務對帳的責任切分:

  • 資料 owner:誰擁有那張表 / 那組欄位、誰負責解釋為何資料長那樣。資料 owner 通常是寫入該表的服務團隊。
  • 對帳作業 owner:誰負責定義 reconciliation query、跑、看結果。可能跟資料 owner 是不同人(例如平台團隊跑對帳、業務團隊擁有資料)。
  • 差異處理 owner:mismatch 出現後、誰負責決定修復策略。通常跟資料 owner 一致、但跨團隊 mismatch 要先約定誰主導。
  • 修復執行 owner:實際下 SQL / call API 的人。可能跟差異處理 owner 不同(差異處理 owner 決定修復策略、修復執行 owner 動手執行)。

四個 owner 在簡單場景可以是同一人、在複雜跨團隊場景必須清楚分派:「明確 owner」指的是 對每一段流程 都有人能簽收,只有對帳這件事整體有 owner 並不夠。

跨組織對帳的特殊問題:跟外部 provider(金流、物流、SaaS supplier)對帳時、對方不見得會接受我方的對帳結果、也不見得會給差異列表。常見處理:

  • 自己跑兩份對帳:內部紀錄對 provider report(每天)、內部紀錄對 provider API(即時抽樣)、兩份結果不同代表 provider report 本身有問題。
  • 約定差異仲裁流程:簽 SLA 時就寫清楚、mismatch 出現後雙方各保留多久的資料、誰先給對方檢視。
  • 不能依賴 provider 修:金流 provider 通常只負責對帳、不負責修我方的 DB,修復由我方負責。

跟 Backup / PITR 整合

當修復必須回到某個過去的時間點、需要 backup 配合:事故之後用備份把資料回復到正確狀態。備份本身怎麼防被攻擊(備份的權限獨立性與 attack surface)見 1.5 Red Team 備份段。

Snapshot-based recovery

  • 整個 cluster 從 N 小時前的 snapshot 還原
  • 影響:所有 其他 資料也回到那個時間點
  • 適合:catastrophic data corruption

PITR(Point-in-Time Recovery)

  • snapshot + WAL / binlog replay 到指定時間
  • 影響:跟 snapshot-based recovery 一樣是整個 cluster 回到過去,指定時間之後的所有寫入(包含跟事故無關的表)都不在回復結果裡;差別在時間點可以指定到誤操作發生之前的那一刻,不限於 snapshot 拍下的那幾個時刻
  • 適合:「3 小時前 admin 誤刪一張表」這類精準回放;只要救回那一張表時,PITR 的目標可以是另一台新的 instance,回復完成後再把那張表的資料匯回正式環境

Logical backup(mysqldump / pg_dump)

  • 整個 schema + data 的 SQL script
  • 適合:跨環境遷移、特定表回復、小規模修復

Continuous archive

  • WAL / binlog 持續備份到 S3 / GCS
  • 一直可以回放到 任何時間點
  • 對應 Genesys 99.999% — 高可用需要快速 PITR

Recovery 時的對抗壓力

PITR / snapshot recovery 不是純技術問題、會在事故當下面對「為了快、要不要跳檢查」的取捨。對應 VMware ESXiArgs 2023 ransomware recovery pressure — 虛擬化平台勒索後、團隊在 營運壓力 跟 資料可信度 之間擺盪:snapshot 是否乾淨、回復後資料是否被污染、跳過 integrity check 換 RTO 是否可接受。判讀重點:recovery 流程要事前 演練 過、否則事故當下不知道要 verify 什麼、容易在壓力下接受被污染的 backup。對應 8.5 Incident Decision Log、事故當下的取捨要寫進 decision log。

RTO/RPO 跟業務可接受中斷的對照表

業務可接受中斷時間是 RTO/RPO 的判讀對照基準。RTO(Recovery Time Objective、多久能恢復)跟 RPO(Recovery Point Objective、最多丟多少資料)是技術指標、要對照業務側的可接受上限才能判斷夠不夠。常見錯誤是把 RTO/RPO 訂在「技術上能做到的最佳值」、忽略業務實際的容忍範圍。

對應 Change Healthcare 2024 — 「定義核心流程的 RTO / RPO、讓資料修復時間跟業務可接受中斷時間明示對照、不藏在直覺」。事故當下發現「DB 能 2 小時恢復、但業務只能容忍 30 分鐘中斷」、來不及補救。

對照表設計:

業務流程RTO(技術)業務可接受中斷落差處理
用戶登入30 分鐘5 分鐘加 standby region failover
訂單寫入1 小時30 分鐘加 outbox + replay
報表查詢4 小時1 天RTO 充裕、不需投資
對帳 batch8 小時3 天RTO 充裕
付款1 小時0(不能停)必須 active-active

關鍵情境延伸:

  • 付款(必須 active-active):業務可接受中斷為 0、單一 region failover 都不能用(failover 期間用戶看到失敗)、必須多 region 同時寫入、靠 Aurora DSQL / Spanner / Cosmos DB multi-region write 撐。設計權衡是 跨 region 寫入延遲 跟 對帳一致性的特殊處理(同一筆款項可能在兩個 region 各被處理一次、要靠 idempotency key 去重)。詳見 1.11 全球分散式 OLTP。
  • 訂單寫入(outbox + replay):30 分鐘容忍區間夠用 outbox pattern — 訂單寫進 DB 同步寫進 outbox table、async worker 把 outbox event 推下游。即使下游中斷、訂單本身已落地、event 可在恢復後 replay。設計權衡是 outbox table 的儲存成本跟 replay 邏輯的冪等性、跟 03 訊息佇列模組 的 outbox pattern 整合。
  • 用戶登入(standby region failover):5 分鐘容忍意味 自動 failover 必須在這時間內完成、人類介入做不到、要靠 DNS health check + Route 53 / Cloudflare 自動切流。權衡是 standby region 平時付閒置成本、跟 active-active 比、便宜但 failover 時有 1-3 分鐘延遲跟 cache miss。

RTO 與業務可接受中斷之間的落差是 投資訊號、不是「忽略它」。RTO > 業務容忍時、要嘛降 RTO(加 HA / DR 投資)、要嘛跟業務協商提高容忍(通常不接受)。

判讀重點:對照表要每年 review。業務模式變了(例如從 B2C 變 B2B 客服 SaaS)、容忍時間會大幅縮短、RTO 必須跟著降。

事故角色預定義

DB 事故當下、資安處置 跟 業務連續性處置 要 分軌並行、不是線性執行。這要求事先有 dual-track IC(Incident Command)角色、不是事故當下臨時拉人。

對應 Change Healthcare 2024 — 「技術處置與業務處置分軌並行的前提是事先有 dual-track IC 角色」。沒事先定義、事故當下會出現「資安 team 在隔離系統、business team 在喊客戶等不及」、兩條軌道互相干擾。

IC 角色定義(以下為通用 IC 模型、非案例直接揭露:在 dual-track 的技術、業務兩條軌道之外,再加上協調、資料與對外溝通三個角色;具體角色細分視組織規模調整):

軌道角色責任
技術軌道Tech IC漏洞修補、系統恢復、技術決策(rollback / restart 等)
業務軌道Business IC客戶溝通、降級流程啟動、合規通報、業務 fallback
協調軌道Overall IC兩條軌道協調、跨軌道決策、對外發言
資料軌道Data IC資料完整性驗證、修復決策、audit chain
Comms 軌道Communications Lead內部通報、外部公告、media 應對

Overall IC 跟一般技術 IC 的差異:一般 IC 主要在技術軌道內決策(要不要 rollback、要不要重啟);Overall IC 額外承擔 跨軌道仲裁 責任 — 當 Tech IC 想停服務止血、Business IC 想保服務維持收入、兩者衝突時、由 Overall IC 拍板。這個角色需要對技術跟業務都有足夠理解、不能只懂一邊;通常由高階工程主管或 CTO/VP Eng 兼任、不是輪值的 oncall。

Data IC 的特殊角色:跟其他軌道相比、Data IC 的決策時間軸最長 — 技術修復可能 1 小時完成、但 資料是否被污染、要不要 PITR、PITR 到哪個時間點 可能要 24-72 小時驗證。Data IC 不能被 Tech IC 跟 Business IC 的「快快上線」壓力推動、必須有獨立判斷權。實務上常見的失誤是讓 Tech IC 兼任 Data IC、結果為了 RTO 跳過 integrity check、事後發現資料污染擴大。

事先準備:

  • Primary + backup 雙人配置:每個角色都要有 primary + backup、避免單人不可用(休假、生病、被另一事故占住)讓事故當下卡住。實務上要有 指定流程 而非「臨時找誰」、避免事故當下浪費 30 分鐘喬人。
  • 責任寫進 runbook:runbook 要列出每個角色該做什麼決策、不該做什麼決策(避免越權)。事故當下查職位、會在最壓力大的時候做組織決策、出錯機會高。
  • 定期 tabletop 演練:演練的重點不是「技術修復對不對」、是「角色交接是否流暢」。Overall IC 跟 Tech IC 之間的權限邊界、Data IC 何時介入、Comms Lead 何時對外發言、都要在演練中試出來。
  • 跨時區 follow-the-sun 輪值:B2B SaaS 跟全球業務、事故不分時區、要有 24/7 覆蓋。單一時區團隊在事故發生在凌晨時、人力不足或反應慢、會放大事故代價。

判讀重點:DB 事故不只是技術事件、會成為 跨多軌道 的事件。角色預定義是組織能力、不是技術能力、但缺它會放大技術事故的代價。

對應 8.5 Incident Decision Log 跟 7.13 Security Routing — 角色預定義是這些跨模組工作的前置。

Evidence Handoff

資料修復的 evidence handoff 要能支援 release gate 與 incident review。

欄位內容
Sourcereconciliation query、provider report、audit log
Time range差異發生窗口與修復窗口
Query linkmismatch sample、修復前後驗證
Ownerdata owner、service owner、reviewer
Data quality抽樣覆蓋率、延遲、未覆蓋資料
Known gap尚未確認的 provider callback、低流量 tenant

這份 handoff 要進入 4.20 Observability Evidence Package 與 8.22 Incident Evidence Write-back。

判讀訊號

訊號判讀重點對應動作
對帳差異率持續上升上游邏輯有 bug、或時間窗對齊問題修上游 + 確認對帳時間窗
同筆資料對帳 run-to-run 結果不同對帳 query 沒處理 in-flight 資料邊界排除最近 N 分鐘、或允許收斂多跑幾次
修復後不一致再次出現沒修根因、只修了 symptom找根因、增加 invariant check
修復影響超出預期範圍mapping 規則錯誤、二次污染立即停止修復、回退
修復沒 dry-run 直接執行流程違規、事後無法佐證影響範圍事後 audit、把 dry-run 列入 gate
Recovery 後 derived state 仍錯重建 derived 時 source 還沒修先修 source、再重建 derived
Audit log 缺欄位事故時無法追究、難 rollback補 audit schema、加 reviewer 欄位
高權限帳號在非 incident 時段啟用可能誤用或攻擊面、break-glass 沒回收立刻檢查 audit log、回收存取憑證
跨服務 mismatch、各方都推卸對帳 owner 沒分派、責任空白補資料 owner / 對帳 owner / 執行 owner
anomaly alert 跟對帳 mismatch 混報兩種訊號性質不同、reviewer 無法判讀拆 dashboard、deterministic 跟 statistical 分開

常見誤區

把對帳當成「定期 batch job」、不關心 當下不一致。實時對帳跟 batch 對帳是 不同工具、不能互相替代。

把資料修復當成「一個工程師動手改」、沒 audit、沒 review、沒 rollback。資料修復本質是 production 操作、跟 deploy 同等嚴格。

把 PITR 當成 常規修復工具。PITR 影響大、適合 catastrophic event、不適合單筆資料修復。

把 derived state 不一致跟 canonical state 不一致 混在一起 處理。derived 是 再生 的、canonical 是 永久 的、處理流程完全不同。

把對帳結果跟 anomaly detection 結果放同一份報告。對帳結果是 deterministic、anomaly detection 結果是 statistical、混報會讓 incident reviewer 無法判斷必修跟可疑。對帳 mismatch 要有獨立追蹤面板、anomaly 走另一條路徑。

跳過 dry-run、直接 update。即使單筆修復、也要先 select 看到當前 row、確認 WHERE 條件命中預期。incident 壓力下尤其容易跳、結果反而把單點問題擴成批次污染。

把修復權限當常駐權限發放。長期 write 權限放在工程師帳號上、會在事故無關時段被誤用、且事後無法區分「正常工作」跟「非法修復」。修復權限要時效化、申請即用即收。

案例對照

案例reconciliation 重點
Zomatomigration 期間用 shadow read 持續對帳
DraftKings體育博彩 ledger、結算後對帳
Standard Chartered跨市場銀行、每市場獨立對帳

實體服務討論承接點

實體資料庫文章要承接本篇的 reconciliation 與 data repair 責任。PostgreSQL、MySQL、MSSQL 或其他資料庫的差異、應放在它們如何產生 validation query、保留 audit trail、支援 point-in-time recovery、處理 replica lag 與控制修復權限。

若服務需要高頻對帳、後續文章要比較查詢成本、索引策略與 replica 讀取延遲。若服務需要高風險資料修復、後續文章要比較 transaction log、backup/restore、row-level audit 與權限分離。若服務需要跨系統補償、後續文章要把資料庫能力接到 queue replay 與 incident decision log。

跨模組路由

  1. 與 1.3 的交接:transaction boundary 決定哪些不一致可避免 — Transaction Boundary
  2. 與 1.5 的交接:audit 跟 access control — Red Team Data Layer
  3. 與 1.7 的交接:migration 後驗證 — Schema Migration Rollout Evidence
  4. 與 1.8 的交接:canonical vs derived 是修復的前置 — State Ownership
  5. 與 3.8 的交接:消息重放與補事件 — Queue Consumer Retry / Replay
  6. 與 4.20 的交接:evidence handoff — Observability Evidence Package
  7. 與 7.7 的交接:audit trail — Audit Trail and Accountability Boundary
  8. 與 8.22 的交接:incident evidence write-back — Incident Evidence Write-back

下一步路由

要處理 migration 造成的資料差異、接著讀 1.7 Schema Migration Rollout 證據。要處理事件漏發造成的副作用修復、接著讀 3.8 Queue Consumer Retry 與 Replay Handoff。要設計跨服務 reconciliation 跟 saga compensation、接著讀 1.3 Transaction Boundary 的 Saga 段。