Immutable Settlement Contract¶
概念概覽
核心約束¶
核心知識¶
核心約束¶
SettlePlayer(或任何結算 RPC)必須只讀 snapshot 資料 和 frozen_config,不能讀取 live Luban config(或任何 mutable 的設定源)。
違反後果¶
場景:
1. 玩家於 T1 發起結算,使用 config-v1(獎勵 A)
2. 管理員於 T2 修改 Luban config 為 config-v2(獎勵 B)
3. 因故需要 rerun/replay T1 的結算
4. 若讀 live config → 執行 config-v2 → 結果與 T1 不同 → 破壞 immutable settlement 契約
Immutable Settlement 的三個要件¶
- Snapshot 封存:結算輸入(玩家狀態、活動進度)必須在結算時點 snapshot,replay 讀 snapshot 而非 live 資料
- Frozen config:獎勵規則、倍率等設定必須 frozen 至結算記錄,replay 讀 frozen_config 而非 live Luban
- Deterministic execution:相同輸入 → 相同輸出,replay 可驗證原始結果
與冪等性的關係¶
Immutable settlement 是比冪等性更強的保證:冪等性只要求「同一操作不重複執行」,immutable settlement 要求「任何時間點的 replay 都能還原原始結果」。
經驗教訓¶
-
Settlement RPC 讀 live config 是架構 bug,不是實作細節,必須在 code review 攔截
-
Frozen config 需要在結算時主動持久化,不能事後假設 config 不變
-
Replay correctness 是 audit/dispute resolution 的基礎,不能妥協
常見陷阱¶
-
假設 Luban config 很少變動,所以讀 live config「應該沒差」
-
Snapshot 只封存玩家狀態,忘記封存 config 版本
-
測試時 config 沒有變動,導致 replay bug 在測試期間看不出來
最佳實踐¶
-
SettlePlayer 設計:輸入 = snapshot_id + frozen_config_version,不接受 live 資料
-
結算記錄應包含完整 config snapshot 或 config version hash,支援事後 audit
相關概念¶
- ClaimMarker Pattern (PostgreSQL Idempotency)
- Dungeon Reward Dual-Path Contract
- RPC Idempotency for Gacha / Random Pack Opening
相關視角¶
以下頁面與本概念共享主題,但從不同角度切入。保留獨立視角同時提供交叉參考:
- Activity Framework Settlement Idempotency — 共享:
idempotency,settlement/ 獨特:activity-framework,transaction - Reward Claims Architecture — 共享:
game-server/ 獨特:reward-system,schema-design - Redis Sorted-Set TTL GC Capacity Bomb — 共享:
game-server/ 獨特:capacity-planning,event-system
來源 Sessions¶
| 日期 | Session | 貢獻摘要 |
|---|---|---|
| 2026-04-01 | 8a1ca078-1c39-4b3a-aec8-0a4b6acb84e9 | 定義 SettlePlayer 必須只讀 frozen snapshot + frozen_config 的不可變結算契約,以及違反此契約的後果 |