136 lines
10 KiB
Markdown
136 lines
10 KiB
Markdown
# 阶段3B实施任务书:固定会话与串行同步
|
||
|
||
日期:2026-09-22。工作方式已由用户调整为:**主会话完成文档与最终审查;另一位AI负责实现和自测。** 本任务书是3B实施的唯一直接入口;完整技术依据仍为《阶段3缓存同步方案与评审-2026-09-22.md》及其独立方案审查。
|
||
|
||
## 1. 当前基线与目标
|
||
|
||
- 阶段2 `PlayerKVClient/PlayerKVModels` 已验收;正式地址固定 `http://s101.local:18080/`。
|
||
- 阶段3A `PlayerKVSyncModels/PlayerKVCacheStore/PlayerKVSyncState` 已验收:主测试86项、独立127断言、Unity编译0错误。
|
||
- 3B目标:把一个已认证且固定的 `S101AccountSession`、一个3A缓存命名空间和一个独立 `PlayerKVClient` 组合为单账号owner;所有Get恢复、Set、Delete和立即票据由单一调度器串行执行。
|
||
- 本批不接 `UserService`、`PlayFabMgr`、`CustomServerMgr`、玩法或全局容器;不访问正式服;不打包。
|
||
|
||
## 2. 所有权和允许改动
|
||
|
||
实施AI拥有:
|
||
|
||
- 新增 `Client/Assets/Scripts/Services/PlayerData/PlayerKVSessionOwner.cs` 及meta。
|
||
- 新增 `Client/Assets/Scripts/Services/PlayerData/PlayerKVSyncCoordinator.cs` 及meta。
|
||
- 新增 `CodexOutput/upserver/KVSyncCoordinatorTests/`,直接编译实际生产源码。
|
||
- 新增自己的实施日志 `CodexOutput/upserver/阶段3B实施记录-2026-09-22.md`。
|
||
- 可最小修改 `KVContractTests/KVContractTests.csproj` 与 `KVSyncTests/KVSyncTests.csproj`:将现有 `PlayerData/*.cs` 通配符改成各阶段实际需要的显式生产文件列表,使原42/86项回归命令在新增3B源码后仍能独立运行。不得修改两个目录的Program.cs、既有日志或测试语义。
|
||
|
||
默认禁止修改:
|
||
|
||
- 已验收的3A三个文件、阶段2两个文件、`S101AccountSession.cs`。
|
||
- `UserService`、`PlayFabMgr`、`CustomServerMgr`、`RTService`、玩法、配置、SDK、服务器代码及共享状态/TODO。
|
||
|
||
若实际实现证明3A缺少必要的窄接口,先在实施记录中写明:现象、不能在3B文件内安全解决的原因、最小签名和新增测试;暂停该处并通知主会话。不得先改3A再补说明。不得整批回退或整理工作区。
|
||
|
||
上述两个csproj例外仅用于固定回归编译边界:`KVContractTests`显式包含阶段2两个文件;`KVSyncTests`显式包含阶段2两个文件及3A三个文件。3B自有测试显式包含阶段2、3A、3B生产文件和 `S101AccountSession.cs`。禁止用额外MSBuild命令行注入来掩盖原项目本身已无法构建。
|
||
|
||
## 3. 必须形成的公开语义
|
||
|
||
具体类型名可小幅调整,但以下行为和信息不能丢失。
|
||
|
||
### 3.1 Owner创建
|
||
|
||
提供异步创建入口,输入至少包括:
|
||
|
||
- 已认证 `S101AccountSession`;创建时固定其 `ApiBaseUri`、规范化 `PlayerId` 和当前 `CookieContainer`。
|
||
- 缓存根目录、调用方不可变epoch、`Func<long,bool> isCurrent` 或等价归属检查。
|
||
- owner生命周期token。
|
||
|
||
创建时验证session非null、已认证、cookie非null、账号/API与缓存归属一致。使用捕获的CookieContainer创建owner专属 `HttpClientHandler`:`UseCookies=true`、`AllowAutoRedirect=false`,再创建owner专属HttpClient和已验收的PlayerKVClient。owner不Dispose认证session,不调用session.Login,不复用CustomServerMgr transport。
|
||
|
||
测试需要internal注入HttpMessageHandler/transport factory及可控调度钩子;生产公开入口不能允许任意运行期换Cookie或换账号。
|
||
|
||
创建流程:打开3A状态 → 获取操作租约 → `GetAsync(null, ownerToken)` → 再核对epoch/isCurrent/session归属 → 成功才 `Reconcile`。Get失败调用明确的read-failure路径;禁止将失败映射为 `{}`。创建失败必须收尾HttpClient和状态锁;返回结构化错误,不打印存档值、Cookie或响应正文。
|
||
|
||
### 3.2 Coordinator编辑与票据
|
||
|
||
提供:
|
||
|
||
- 远端Set编辑、localOnly编辑、Delete;返回3A写入结果及远端编辑票据。localOnly不创建远端票据。
|
||
- `FlushAsync`:捕获调用时水位,只处理该水位内版本;同一owner任何时刻最多一个变更HTTP在途。
|
||
- `WaitForTicketAsync(ticket, cancellationToken)`:取消只停止该等待者,不撤销已持久化pending、不取消共享Flush或同批其他票据。
|
||
|
||
票据至少固定owner实例/epoch、key和sequence。最终状态必须可区分:`Confirmed`、`Superseded`、`Blocked`、`Unknown`、`Cancelled`。不得用单个bool掩盖Unknown/Superseded。
|
||
|
||
- 精确sequence被HTTP成功且确认快照落盘:Confirmed。
|
||
- 尚未发送的旧sequence被同key新版本覆盖:Superseded。
|
||
- 进入unknown、冲突、认证失效、存储错误或owner关闭:按事实返回Unknown/Blocked/Cancelled,不能误报Confirmed。
|
||
- Wait调用者取消:该等待返回Cancelled,但查询同一票据的事实状态仍由协调器继续更新。
|
||
|
||
### 3.3 单通道调度
|
||
|
||
- 初始化Get、恢复Get、Set、Delete通过同一个调度入口串行;不得并行发两个KV变更请求。
|
||
- `FlushAsync`在开始时记录最大sequence水位。循环调用3A `BeginBatch(watermark)`;空批次结束。
|
||
- Set批将batch items转为字符串字典;Delete批转为key列表。请求数据必须来自已持久化batch快照。
|
||
- HTTP 200后调用3A `ConfirmBatch`,只有确认快照落盘成功才完成票据。
|
||
- HTTP失败后按 `PlayerKVResult.MayHaveCommitted` 调用3A `FailBatch`;401/403同时标记认证失效。任何MayHaveCommitted=true都冻结为Unknown,不自动重试。
|
||
- 只有有正面证据“未进入传输”时可按false失败并允许以后重新Get;不要根据400/401自行推断未写。
|
||
- Flush汇总至少报告Confirmed/Superseded/Blocked/Unknown数量及最终错误类别;调用者取消等待不成为共享HTTP token。
|
||
|
||
### 3.4 原子准入、失效与Close
|
||
|
||
Owner内需要一个短临界区统一完成:检查closing/closed、登记实际操作、取得租约。操作退出必须finally归还。禁止“先检查状态,稍后登记”。
|
||
|
||
`CloseAsync`:
|
||
|
||
1. 在同一准入门内原子置closing,禁止新操作;重复调用返回同一个关闭任务。
|
||
2. 退出锁后取消owner token;取消回调可能同步执行,不能持锁Cancel。
|
||
3. 结束尚未完成的被动票据等待;等待者本身不算必须drain的实际操作。
|
||
4. 等待已登记的初始化/调度/磁盘操作全部退出,不持admission/state/dispatch锁等待。
|
||
5. 最后依次释放协调器transport、3A state/file lease和owner资源,只执行一次。
|
||
|
||
Close不隐式Flush。已发送写被Close取消时仍可能提交,必须保留active/unknown证据;不能为了让新owner快速打开而强制释放锁。若transport不响应取消,关闭任务保持未完成,同命名空间新owner应Busy。
|
||
|
||
每个await前后检查owner epoch、`isCurrent(epoch)` 和lifetime。旧成功、旧401、迟到取消只能更新旧owner命名空间的可靠证据,不能向新owner发布事件、不能换用新Cookie重发、不能触发无归属全局重登。
|
||
|
||
## 4. 结果模型和日志要求
|
||
|
||
- 结果模型可放 `PlayerKVSyncCoordinator.cs`;不要为两个简单enum额外扩大生产文件。
|
||
- 不把 `PlayerKVError` 直接当完整同步状态;保留HTTP错误和缓存/票据状态两个层次。
|
||
- 日志/异常只允许账号是否匹配、epoch、数量、sequence、HTTP状态/错误类别;禁止存档值、完整请求JSON、Cookie和服务器正文。
|
||
- 任何异常都必须转换为结构化结果或使创建失败并可靠收尾;后台任务异常不得成为未观察异常。
|
||
|
||
## 5. 实施顺序和检查点
|
||
|
||
实施AI必须按顺序更新自己的实施记录:
|
||
|
||
1. 固定接口草图、锁顺序及文件边界;确认不需要改3A。
|
||
2. 实现Coordinator纯调度和结果模型,先配HttpMessageHandler替身测试。
|
||
3. 实现SessionOwner创建/归属/Close,补并发交错测试。
|
||
4. 完整离线测试、原3A/阶段2测试回归、Unity编译。
|
||
5. 生成最终源码hash、列明未执行项,停止开发并交还主会话审查。
|
||
|
||
不得自行进入3C、访问s101、接游戏入口、修改状态/TODO、打包、提交或发布。
|
||
|
||
## 6. 3B验收矩阵
|
||
|
||
至少覆盖:
|
||
|
||
1. 创建:未认证、错误账号/API、Get 200空字典、Get401/403/协议/网络/取消、打开缓存Busy/RecoveryRequired;失败不留锁/transport。
|
||
2. Cookie:两个owner各自独立Cookie/handler;捕获后调用方切session或Dispose,旧owner不借新Cookie;禁止自动重登。
|
||
3. 串行:Set/Delete交错、批10、51+key、Flush水位、连续编辑时Flush可结束,最多一个HTTP在途。
|
||
4. 多版本:v1在途产生v2;v1确认后v2保留;尚未发送v1被v2替换时v1票据Superseded。
|
||
5. 票据:两票据同批,其中一个等待取消不影响HTTP/另一票据;取消Flush等待不撤销目标;重复等待得到一致事实。
|
||
6. 故障:预发送取消、发出后取消、超时、网络、400/401/403/500、HTTP200后磁盘确认失败、active写前磁盘失败;逐项核对unknown、pending和票据。
|
||
7. 换号/代际:101请求在途→失效→102 owner;101迟到200/401不改变102状态,不使用102 Cookie,不发布全局事件。同号新epoch同样隔离。
|
||
8. Close:检查与登记交错、并发Update/Flush/Close、Cancel同步回调重入、重复Close、transport延迟/忽略取消、文件锁仅实际收尾后释放。
|
||
9. 回归:KVContractTests 42、KVSyncTests 86保持通过;新增测试直接编译实际生产源码;Unity编译0错误,新文件无新增警告。
|
||
|
||
允许测试使用合成API和Cookie、任务目录缓存;不允许正式服写入或真实业务key。测试必须有确定超时,不能无限等待。
|
||
|
||
## 7. 实施AI交付格式
|
||
|
||
完成时只报告:
|
||
|
||
- 修改/新增文件及SHA256。
|
||
- 新增测试数量、分组、命令和退出码。
|
||
- 阶段2/3A回归及Unity编译结果。
|
||
- 独立说明哪些是合成传输证据,哪些尚未验收。
|
||
- 已知限制或任何偏离任务书的点。
|
||
|
||
主会话收到交付后将逐文件审查、复跑关键测试、检查工作区差异,并决定通过、要求返工或补裁决。实施AI不得自行宣布3B最终验收通过。
|