Go Transaction Atomicity Patterns¶
概念概覽
三種典型 Transaction 邊界錯誤¶
核心知識¶
三種典型 Transaction 邊界錯誤¶
1. Read Outside Transaction(Stale Snapshot)¶
AdminRemoveItem 在 tx 外讀取 item.Quantity,開 tx 後資料可能已被其他 goroutine 修改。
規則:需讀寫同一筆資料時,read 必須在 transaction 內部。
2. 多步驟操作非 Atomic¶
useInBattleItem 扣背包 + UpdateItemUsageLog 分屬兩個操作,失敗時無法一起 rollback。
規則:「扣資源」與「記錄扣除原因」必須在同一個 transaction。
3. GDPR Soft-Delete 不 Atomic¶
gdpr_service.go soft-delete 資料列與建立 deletion task 分兩步執行,若中間崩潰會產生孤立的 soft-deleted 記錄(task 不存在,資料不會被真正刪除)。
規則:任何「狀態標記 + 後續任務建立」的組合都必須在同一 DB transaction。
反模式識別¶
// WRONG: read outside tx
item, _ := repo.GetItem(ctx, itemID) // <-- stale snapshot
tx, _ := db.Begin(ctx)
tx.UpdateItem(ctx, item.ID, item.Quantity-1)
// CORRECT: read inside tx
tx, _ := db.Begin(ctx)
item, _ := tx.GetItemForUpdate(ctx, itemID) // FOR UPDATE lock
tx.UpdateItem(ctx, item.ID, item.Quantity-1)
經驗教訓¶
-
「讀取後寫入」模式必須用 SELECT FOR UPDATE 或將 read 移入 tx,否則是 TOCTOU 問題
-
GDPR 合規操作(soft-delete + task)的原子性在 scale 下尤其重要,崩潰恢復依賴它
-
扣資源與記錄日誌分開 commit 是常見反模式,排查時優先檢查這類配對操作
常見陷阱¶
-
CAS retry 模式(如 GiveGift)若 retry 前未確認前次操作是否真的失敗,可能重複消耗
-
tx 持有時間增加(將 read 移入 tx)有效能代價,需評估 lock contention
最佳實踐¶
-
讀寫同一筆資料:read 必須在 tx 內(FOR UPDATE)
-
狀態標記 + 後續任務:同一 tx
-
扣資源 + 記錄日誌:同一 tx
相關概念¶
來源 Sessions¶
| 日期 | Session | 貢獻摘要 |
|---|---|---|
| 2026-03-17 | 10eef6a0-3a55-4d63-981c-6bc79aff1880 | 歸納出 Go 後端服務中三種常見的 transaction 邊界錯誤模式,並給出修正規則 |