修复 AGC 换号后整项目快照重传导致的日志刷屏与客户端超时
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m20s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m57s
Project CI / Backend tests (pull_request) Successful in 4m52s
Project CI / Native shell tests (pull_request) Successful in 5m59s
Project CI / Frontend tests (pull_request) Successful in 1m56s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m46s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 9m0s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m24s
Project CI / Repository checks (pull_request) Successful in 1m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m20s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m57s
Project CI / Backend tests (pull_request) Successful in 4m52s
Project CI / Native shell tests (pull_request) Successful in 5m59s
Project CI / Frontend tests (pull_request) Successful in 1m56s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m46s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 9m0s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m24s
Project CI / Repository checks (pull_request) Successful in 1m50s
- 项目快照索引升级为 schema v2:按账号分桶保存基线,换号不再让其它账号的基线失效,也不会被其它账号覆盖 - 同步只读取并写回当前登录账号的基线,其它账号的基线原样保留 - 读取时按 JSON 版本判定并迁移 v1 单账号索引到它自己记录的 userId 桶,格式升级不额外触发一次重传 - read_local_project_snapshot_state 增补 baselineCount / baselinePresent,fileCount / syncRevision / syncedAtMs 改为当前登录账号的基线口径 - 新增与改写用例:账号切换后基线互不覆盖、切回旧账号差异为空、v1 索引迁移进对应账号桶 - 同步更新快照主规范、快照里程碑文档、shared-memory decision-log 与 pitfalls
This commit is contained in:
@@ -9329,3 +9329,14 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 影响面:`server-rs/crates/api-server/src/{config.rs,modules/game_distribution.rs}`、`server-rs/crates/shared-contracts/src/game_distribution.rs`、`packages/shared/src/contracts/gameDistribution.ts`、`src/components/game-distribution/gameDistributionGuards.ts`(含新增测试)、`deploy/{nginx,container,env}`、`scripts/check-game-distribution-media-e2e.mjs`、`package.json`、平台与运维主规范。
|
||||
- 边界:SpacetimeDB 表结构与公开契约字段不变(`entryUrl` 仍是 string),只是取值从绝对 URL 变为相对路径;历史版本已冻结的绝对值不改写,admin 页与详情页展示口径不变。线上 dev / release 的 nginx 已按同源路径改动并 reload,`/etc/genarrative/api-server.env` 已删除模板变量;api-server 未重启,新写入要等下次重启。
|
||||
- 验证:`cargo check -p api-server --tests`、`cargo test -p api-server game_distribution`(27 passed)、`cargo fmt --all --check`、`npx vitest run src/components/game-distribution`(57 passed)、`npm run check:nginx-spa-routes`、`npm run check:encoding`(5060 文件)、`npm run check:doc-index`、`git diff --check` 全部通过;三份 nginx 模板渲染后 `nginx -t` 语法通过;dev 线上实测 `/games/game_2dcd…4955/` 与 `./assets/index-2Ws3zHlS.js` 均 200。
|
||||
|
||||
## 2026-09-24 AGC 项目快照索引按账号分桶:换号不再整项目重传
|
||||
|
||||
- 背景:AGC 客户端在项目打开期间把本地工程增量上传到 OSS,判定依据是 `<AppData>/project-snapshots/<projectId>/index.json`。该文件整份只保存一个账号的基线,同步前用 `previous.user_id == session.user_id` 判等,不等就换成空基线;而远端对象键第一段正是 `userId`,于是换号被等价成「本机没有基线」。issue #504 的真机复现(CDP attach dev 客户端 + 点「春卷冲刺」):账号 A 留下的基线与当前账号不匹配 → 一次性重传 2311 个文件,约 3 分钟、2311 次 `POST /api/agc/project-snapshots/files`,其中 2306 次服务端 HEAD 命中纯白跑;同窗口内 `/api/runtime/frontend-config`、`/api/llm/models`、`/api/profile/recharge-center` 在服务端 200 且 ≤61ms 的情况下被客户端报 15s 超时(同进程 IPC 被重传压垮),并触发 #490 的「检查失败」钉死。本机 6 个索引文件里有 4 个不同 `userId`,09-23 19:47、09-23 20:47、09-24 10:57 三次同形态全量重传。
|
||||
- 决策:索引文件升级为 schema v2,结构改为 `baselines: { userId → 该账号基线 }`;同步只读当前登录账号的桶,写回也只替换自己那一个桶,其它账号的基线原样保留。基线字段与 v1 一致(`syncRevision` / `syncedAtMs` / `projectName` / `pendingFiles` / `files`)。某账号在本机首次同步该项目仍然全量上传,这是必要行为而不是漏洞。
|
||||
- 决策(迁移):读取时按 JSON 值判定版本,v1 文件按其记录的 `userId` 迁移进对应桶(`userId` 为空或 `projectId` 不符时丢弃),格式升级本身不产生额外重传;v1 缺少的 `projectName` / `pendingFiles` 迁移后仍表示「完整性未知」,不伪造已完成。文件保持 v1 形态直到下一次成功同步写回 v2。
|
||||
- 决策(诊断):`read_local_project_snapshot_state` 增补 `baselineCount` 与 `baselinePresent`,`fileCount` / `syncRevision` / `syncedAtMs` 改为当前登录账号的基线口径——此前这三项会把另一个账号的基线报成本账号的。
|
||||
- 影响面:`apps/ai-game-creator-shell/src-tauri/src/project_snapshot/{index.rs,mod.rs,tests.rs}`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/plans/【里程碑】AGC项目定时快照上传-2026-09-17.md`、本文件与 pitfalls。
|
||||
- 边界:不新增路由、不改服务端、不改公开契约;远端对象键与清单结构不变;索引仍是 AppData 私有文件、不进项目目录;旧客户端读到 v2 文件会判「版本不符」→ 该账号一次全量,属可接受回退代价。
|
||||
- 未覆盖:一次「项目 + 新账号」的新组合仍会把项目全量发给服务端(服务端逐个 HEAD 跳过);要彻底消掉这批白跑请求,需要客户端可读的远端清单或批量存在性探测,另开事项。
|
||||
- 验证:`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml project_snapshot`(含「账号切换后基线互不覆盖」「切回旧账号差异为空」「v1 索引迁移进对应账号桶」三条新增/改写用例)、`cargo fmt --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- --check`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
@@ -5947,3 +5947,12 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **处理(现行口径)**:`createChannelConfig()` 从基线 `src-tauri/tauri.conf.json` 读完整 client 窗口对象后展开、只覆盖 `title`(`readBaseClientWindow()`),渠道配置不得再出现"只写 `title`"的窗口对象。新增守卫:`build-release.test.mjs` 用同语义的 merge patch 复现 Tauri 合并并断言 `label=client` / `decorations=false` / 1280x800 / min 1280x720 且承载 `http:default` 的 capability 必须包含该 label;`check-config.mjs` 增补基线 `decorations !== false` 失败关闭。
|
||||
- **验证**:`node --test apps/ai-game-creator-shell/scripts/build-release.test.mjs scripts/cargo-features.test.mjs scripts/release-oss.test.mjs scripts/prepare-macos-codex.test.mjs`(60/60)、`node apps/ai-game-creator-shell/scripts/check-config.mjs` 通过;`createChannelConfig('dev', …)` 实测输出含 `label: client` 与 `decorations: false`。修复后的安装包尚未重新构建与安装,真机观感与登录链未复核。
|
||||
- **关联**:`apps/ai-game-creator-shell/scripts/build-release.mjs`、`apps/ai-game-creator-shell/scripts/build-release.test.mjs`、`apps/ai-game-creator-shell/scripts/check-config.mjs`、`apps/ai-game-creator-shell/src-tauri/capabilities/main.json`、`docs/technical/【技术方案】AGC客户端更新检查与下载-2026-08-31.md`。
|
||||
|
||||
## 2026-09-24 AGC 换号后整项目快照重传:后端日志刷屏 + 其它接口 15s 超时
|
||||
|
||||
- **现象**:打开 AGC 项目后客户端短时间无响应,`/api/runtime/frontend-config`、`/api/llm/models`、`/api/profile/recharge-center` 报「请求超时(15000 ms)」;同时本地 api-server 日志被 `POST /api/agc/project-snapshots/files` 刷屏(全是 200、`latency_ms` 58~61ms、每 60ms 一条)。紧接着项目列表还会被钉成「检查失败」(#490)。
|
||||
- **原因**:本地快照索引整份只保存一个账号的基线,同步前按 `user_id` 判等,不等就换成空基线 → 换号后打开项目即整项目重传(春卷冲刺 2311 个文件、约 3 分钟)。对象键带内容摘要、服务端 HEAD 命中即 `200 + skipped`,这批请求全是白跑却完全静默,只有服务端日志量能看出来。真正超时的那一层在客户端:同一窗口服务端对三个接口都是 200、≤61ms,而客户端 webview 的 Tauri 定制协议 IPC 在重传压力下大面积失败(`IPC custom protocol failed … TypeError: Failed to fetch`,刷屏时 ≈0.7 次/秒,而空闲时段 847 行日志里只有 59 次;每次失败还写一行 ~330 字节日志),响应回不到 renderer,JS 侧 15s 计时器先到。
|
||||
- **处理(现行口径)**:索引升级为按账号分桶(schema v2,见 decision-log 2026-09-24「AGC 项目快照索引按账号分桶」),换号只让该账号自己没有基线,切回旧账号不再重传。
|
||||
- **排障口径**:遇到「后端日志刷屏 + 客户端报超时」这种组合,先把两侧对齐到同一时间窗:客户端 `diagnostics/application.log` 的 `project_snapshot.sync.*`(看 `uploaded=` 是否等于项目文件数、`skippedRemote=` 是否接近它)对 `logs/api-server/api-server-*.log` 里各 route 的 `http.response`(状态码与 `latency_ms`)。客户端报超时但服务端 200 且毫秒级返回,说明堵在客户端侧,别只盯后端。
|
||||
- **验证**:`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml project_snapshot` 里「账号切换后基线互不覆盖」「切回旧账号差异为空」两条用例;运行态可用 `read_local_project_snapshot_state` 看 `baselineCount` / `baselinePresent`(换号前后各持自己的基线)。
|
||||
- **关联**:issue #504、issue #490、`apps/ai-game-creator-shell/src-tauri/src/project_snapshot/index.rs`、`docs/project-memory/shared-memory/decision-log.md`(2026-09-24 同名条目)。
|
||||
|
||||
Reference in New Issue
Block a user