Activity Framework Phase 3 完整實作:ClosureBuilder、Fleet Registry、Config Sync、Admin RPCs(融合版 v2)¶
文檔資訊
- 分類: architecture
- 難度: advanced
- 預估閱讀時間: 14 分鐘
- 標籤:
activity-framework,golang,settlement,idempotency,fleet-registry,config-sync,bundle-admission,proto,twirp,phase3,coding-style,worktree
摘要¶
完整記錄 activity-framework Phase 3 的並行實作流程,包含 W6.1–W6.6 六個子任務的實作決策、Settlement 冪等性討論、RPC 契約落差分析、架構邊界技術見解,以及 coding-style.md 變數命名規則的執行案例(新增)。
關鍵學習¶
-
Settlement double-settle 風險的前提是 handler.SettlePlayer 做了 tx 外不可回滾的副作用(HTTP/MQ/push),framework 本身 tx 包覆是安全的
-
RPC 契約落差要區分「純文件同步」vs「真實語意缺口」:CreateManualActivity 缺少 source_key 的 idempotency 是真實缺口,不只是改名
-
handler fail-fast(DoD 要求 register 階段 fail-fast)更適合 infra 依賴,plugin-style handler 用 runtime ErrHandlerNotRegistered -> Unimplemented 是合理的 framework 邊界
-
GetFleetBundleStatus 排除 unready pod,用 3 consecutive heartbeat failure 偵測後 mark unready
-
Config Sync 用 advisory lock 序列化 + fingerprint 驗證防止並發衝突,content_mutation 會 block 整個 sync
-
ClosureBuilder 用 custom JSON encoder(非 encoding/json)保證 deterministic output,控制 float 格式與 time 格式
-
pkg/config/luban / cmd/nakama 的 build failure 用 git diff --name-status main 確認不是此 branch 引入,屬於 repo-wide merge blocker 而非 activity-framework 責任
-
coding-style.md 規定所有變數以小寫開頭(ctx、userID),subagent 產出的程式碼若出現大寫開頭變數即為違規,需強制修正
技術細節¶
六個並行子任務概覽¶
W6.1 ClosureBuilder
- internal/modules/activity/activity_closure.go:custom deterministic JSON serializer,9 rules 包含 key ordering、array sort by ID、no trailing zeros on floats、RFC 3339 UTC
- ComputeSpecHash(6 provisioning spec fields)與 ComputeContentHash(closure bytes SHA256)
- Circular reference detection 用 DFS with white/gray/black coloring,O(V+E)
- 使用 encoding/json 無法控制 float 格式與 time 格式,故採 custom encoder
W6.2 Fleet Registry
- activity_fleet_registry_postgres.go:RegisterPod, MarkPodReady, HeartbeatPod, MarkPodUnready, DeregisterPod, GetFleetBundleStatus, GetDivergentPods
- RegisterAndStartHeartbeat:10s interval 心跳,3 consecutive failure 後 mark unready
- wiring 用 SetFleetRegistry(fr) setter(post-Wire initialization,per architecture.md section 4.6)
W6.3 Bundle Admission Gate
- ValidateBundleAdmission:對每個 live activity 驗證三件事:handler 已 register、ResolveContentClosure 成功、content_hash 比對
- ContentHash.Valid == false(NULL)視為 warning 非 hard failure,相容早期 activity
- 回傳 AdmissionResult{Ready, Errors, Warnings},由 main.go Phase 3 決定如何處理
W6.4 Config Sync core
- previewConfigSync:build diff → sorted SHA256 fingerprint
- syncActivitiesFromConfig:RunInTx + advisory lock,驗證 fingerprint,依 action 類型處理:create/update_schedule/drift(log-only)/orphan(cancel actions)/content_mutation(block)
- UpdatePendingActionSchedule:prefix-match dedupe_key(pre_warm: / settle:)更新 scheduled_at
W6.5 ForceUpdateContentHash + ListActivities
- GetAllActivities(ctx, source, includeArchived, now):optional source filter + archived exclusion
- forceUpdateContentHash:get activity → resolve closure → SHA256 → update DB → invalidate cache
- ListActivities 需 RoleAdmin,ForceUpdateContentHash 需 RoleSuperAdmin
W6.6 GetSettlementRunDetail
- GetBatchesForRun(ctx, runID):query settlement_batches by run_id ordered by batch_index
- getSettlementRunDetail:compose getSettlementStatus(run + progress)+ GetBatchesForRun(batches)
W8 測試覆蓋(新增)¶
151 tests pass,涵蓋 Bundle Admission Gate(7)、Config Sync Preview(9)、Config Sync Apply(5)、ForceUpdateContentHash(4)、ListActivities(3)、GetSettlementRunDetail(2)、Twirp Adapter(14 subtests)。
Coding Style 違規案例(新增)¶
subagent 在實作中產出了大寫開頭的變數名(違反 coding-style.md),使用者明確指出規則:所有變數一律小寫開頭,包含 ctx、userID 等。這是 subagent 生成程式碼時的常見漏洞,需在 review 時特別注意。
What Changed¶
PR 審查結論修正¶
初步審查提出 6 個問題,最終結論:
- #1 double-settle:撤回,framework tx 包覆正確,唯一前提是 handler 不做 tx 外副作用
- #2:非 bug,接受反駁
- #3 line 922 測試:不是 bug,是 placeholder 行為的測試;但仍是「完整 scope 尚未完成」的佐證
- #4 RPC 契約落差:分成「純文件同步」(Disable/Enable toggle、PauseDispatch→PauseSettlement)和「真實語意缺口」(CreateManualActivity 缺 source_key idempotency、GetSettlementRunDetail/ListActivities/ForceUpdateContentHash 實缺)
- #5 測試覆蓋率:pkg/config/luban / cmd/nakama failure 確認不是此 branch 引入(git diff --name-status main 為空)
- #6 handler fail-fast:改文件,不改 code;plugin-style handler 的 runtime ErrHandlerNotRegistered 是合理邊界
實作完成項目¶
6 個子任務全數完成,151 tests pass(W8 Config Sync tests),W9 文件更新含 RPC 名稱對齊(PauseDispatch→PauseSettlement、UnpauseDispatch→ResumeSettlement、EnableActivity→DisableActivity bool toggle、ClearOverride→OverrideEndTime clear_override=true)與 DoD checklist 標記。RBAC middleware 仍為 Hard Blocker(外部依賴)。
Coding Style 執行(新增)¶
subagent 實作過程中出現大寫開頭變數名,使用者強制要求修正,依據 coding-style.md 規則:所有 Go 變數一律小寫開頭。這個問題在 subagent 並行實作時容易被忽略,需在 code review 階段確認。
So What¶
這是一個複雜的 activity framework 從 Phase 2 升級到 Phase 3 的完整記錄,展示了如何在生產系統中同時推進多個獨立子系統(Fleet Registry、Config Sync、Bundle Admission Gate、ClosureBuilder),並且在過程中做出有依據的架構邊界決策。
新版增加了 coding-style.md 執行案例:subagent 並行實作時容易在變數命名上違規,使用者的執行態度明確——大寫開頭變數是不可接受的錯誤,不是可選建議。這個案例對未來使用 subagent 模式開發有重要的 code review 指導意義。
RBac middleware 仍為唯一 Hard Blocker,staging rehearsal 尚未能進行,但所有 Phase 3 功能本體均已完成。
Trade-offs¶
- handler fail-fast vs runtime error:startup fail-fast 對 infra 依賴合適,但對 plugin-style handler 會過度限制擴充彈性;選擇 runtime ErrHandlerNotRegistered 讓 framework 邊界更清晰
- custom JSON encoder vs encoding/json:encoding/json 在 Go 1.21+ map key 有排序,但 float 格式與 time 格式不可控;custom encoder 增加維護成本但保證 deterministic hash
- content_mutation block 整個 sync:保守設計防止 drift 擴散,代價是需要 ForceUpdateContentHash + 人工審核才能解鎖
- advisory lock + fingerprint:防並發衝突,但 preview→apply 之間若有變更會回傳 ErrConfigSyncConflict 要求重新 preview
- subagent 並行實作:加速開發但需嚴格 code review,變數命名規則等 coding style 問題容易在自動生成程式碼中出現
Try It Fast¶
# 驗證 ClosureBuilder determinism
go test ./internal/modules/activity/... -run TestClosureBuilder -v -count=3
# 驗證 Bundle Admission Gate
go test ./internal/modules/activity/... -run TestBundleAdmission -v
# 驗證 Config Sync (preview + apply)
go test ./internal/modules/activity/... -run TestPreviewConfigSync -v
go test ./internal/modules/activity/... -run TestSyncActivitiesFromConfig -v
# 全模組測試(目標 151 pass)
go test ./internal/modules/activity/... -count=1
go test ./test/... -run Activity -count=1
# 確認 build failure 不是此 branch 引入
git diff --name-status main -- pkg/config/luban cmd/nakama
Recommendation¶
- RBAC middleware 是唯一剩餘 Hard Blocker,在此完成前 staging rehearsal 無法進行,應優先追蹤外部依賴進度
- subagent code review 必查項:變數命名一律小寫開頭(coding-style.md),subagent 生成的程式碼容易違規,尤其在並行任務模式下
- runbook 補充 resume 失敗語意:明確記載
ResumeSettlement可能因 slot 被其他合法 run 佔據而失敗,這不代表系統異常 - handler 副作用規範:在 handler interface 文件中明確標注
SettlePlayer不應做 tx 外不可回滾副作用(HTTP/MQ/push),否則需自行實作冪等 - content_mutation 解鎖流程:確保 runbook 有
ForceUpdateContentHash的使用時機與審核流程說明 - pkg/config/luban / cmd/nakama build failure 雖非此 branch 引入,若 repo merge policy 是 repo-wide green 仍需在 PR 合併前解決,建議開獨立 issue 追蹤