diff --git a/docs/proposals/2026-10-11-collaboration-storage-and-distribution.md b/docs/proposals/2026-10-11-collaboration-storage-and-distribution.md index 3eb7525..318baca 100644 --- a/docs/proposals/2026-10-11-collaboration-storage-and-distribution.md +++ b/docs/proposals/2026-10-11-collaboration-storage-and-distribution.md @@ -8,7 +8,7 @@ product_contract_source: ce-plan-bootstrap # 多人协作存储与分布式演进方案 - Plan -修订:r2.1;确认状态:**已批准(2026-10-11 用户指示「按方案开始改造」)**;推进状态:U1–U7 **代码与测试已合入**(Backend [#164](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/164);Dev [#410](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/410)/[#439](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/439)/[#442](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/442);闭卡 [#424](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/424) 等)。正确性 A1–A7 与 B1 已绿(CI/SELF)。容量合同 B2/B3/B5 contract 与双实例 C2 **本机 SELF 窗口已跑通**(见[容量报告](../records/2026-10-12-u7-capacity-window-report.md));**SELF≠GATE/REPLAY**,双主机 C3、运维演练 C4/C5 未做——**仍不放行第二实例承接写请求**。 +修订:r2.2;确认状态:**已批准(2026-10-11 用户指示「按方案开始改造」)**;推进状态:U1–U7 **代码与测试已合入**(Backend [#164](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/164);Dev [#410](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/410)/[#439](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/439)/[#442](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/442);闭卡 [#424](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/424) 等)。正确性 A1–A7 与 B1 已绿(CI/SELF)。容量合同 B2/B3/B5 contract 与双实例 C2 **本机 SELF 窗口已跑通**(见[容量报告](../records/2026-10-12-u7-capacity-window-report.md))。2026-10-12 用户决定不做容量重放;同日 B6 pgbench、C4 断连、C5 库恢复已有本机 SELF(见[运维演练报告](../records/2026-10-12-u7-ops-drill-report.md)),C5 工件恢复 K4 失败。**SELF≠GATE/REPLAY**,双主机 C3 未做——**仍不放行第二实例承接写请求**。 需求来源:2026-10-11 用户要求完善 SQLite、PostgreSQL/pgvector、Redis 分工;随后确认“10–100 人,多人多项目多会话并行,未来支持多服务分布式”。 需求负责人:weichen;方案编制:Codex;实施及运维负责人:待指定。更新时间:2026-10-12(Asia/Shanghai)。 关联任务:方案与 U1–U7 实施卡已归档;容量收尾见 Dev `.task/feature-u7-capacity-wrap-measure.md`。放行检查单:Dev `docs/release-multi-instance.md`。 @@ -246,8 +246,8 @@ SQLite 本身支持可靠事务与多人经同一应用访问;WAL 提升读写 必需观测:API P95/P99与预期/非预期错误,DB pool wait/连接数/慢查询/锁等待,队列长度/最老年龄,run 租约/过期写拒绝/未知副作用,SSE cursor lag/补查次数,Redis 错误与降级,检索耗时/召回抽样,备份时间与恢复结果。日志只记录 request/run/owner 的安全标识,不记录 DSN、密钥或文档正文。 -r1 为方案与 SELF 调查记录。r2 起:U1–U7 代码与测试已合入且正确性 CI 绿。r2.1:本机双实例 contract 容量窗口(ladder / 60m / 2h)SELF 全过,见[容量报告](../records/2026-10-12-u7-capacity-window-report.md)。本地 SELF 不能冒充 GATE/REPLAY 放行;双主机与运维演练仍缺。部署与第二实例写流量仍未授权。 +r1 为方案与 SELF 调查记录。r2 起:U1–U7 代码与测试已合入且正确性 CI 绿。r2.1:本机双实例 contract 容量窗口(ladder / 60m / 2h)SELF 全过,见[容量报告](../records/2026-10-12-u7-capacity-window-report.md)。r2.2:用户于 2026-10-12 明确不做容量重放;同日完成 B6 pgbench、C4 PG/Redis 断连、C5 物理备份与 pg_dump 恢复的本机 SELF,见[运维演练报告](../records/2026-10-12-u7-ops-drill-report.md)。C4 通过。C5 的库恢复通过(RPO 64 秒,RTO 4 秒),工件上传在未配置对象存储时 panic,K4 失败,产品代码未改。本地 SELF 不能冒充 GATE/REPLAY 放行;双主机仍缺。部署与第二实例写流量仍未授权。 方案静态审阅及修订处置见[审阅记录](../records/2026-10-11-collaboration-storage-plan-review.md)。进程级黑盒相对库级 Manager 测试的漏检教训见[经验条目](../records/2026-10-12-lesson-http-multi-instance-vs-manager-tests.md)。Agent 审阅不替代人工批准或实现验收。 -首选下一增量是 **独立 REPLAY 最小容量集**(至少 `run_load.sh --seed contract --profile ladder --instances 2`)以提升放行证据等级;或其候选项:**C3 双主机验证**(需用户指定第二主机)或 **C4/C5 运维演练**。在 GATE/REPLAY 与 C3 未齐前,不要开放第二实例写。各实现任务启动前复核在途 PR 与依赖 SHA,再按 Dev 的 evidence/task-sync/组合验证流程交付。 +首选下一增量不要自动开工。剩余放行门槛是:独立 REPLAY,或把 C4/C5 快速档接入 CI 形成 GATE(需平台准入);以及 C3 双主机(需第二台机器)。在此之前要先修上传缺陷:未设置 `BLOBSTORE_S3_ENDPOINT` 时,空的 `*S3` 被放进对象存储接口,上传 panic,C5 的 K4 因此失败。GATE/REPLAY 与 C3 未齐、K4 未重测通过前,不要开放第二实例写。各实现任务启动前复核在途 PR 与依赖 SHA,再按 Dev 的 evidence/task-sync/组合验证流程交付。 diff --git a/docs/records/2026-10-12-u7-ops-drill-report.md b/docs/records/2026-10-12-u7-ops-drill-report.md new file mode 100644 index 0000000..60e2d19 --- /dev/null +++ b/docs/records/2026-10-12-u7-ops-drill-report.md @@ -0,0 +1,85 @@ +# U7 运维演练与数据库基线报告(SELF,2026-10-12) + +来源标注:**SELF**(本机隔离栈执行;非 CI GATE、非独立 REPLAY)。用户 2026-10-12 决定不做容量重放。不得据此放行第二实例写流量。 + +## 1. 被测对象 + +| 项 | 值 | +|---|---| +| Backend gitlink | `8e509775d39d1455b69be44803037faa7af844b4` | +| B6 Dev HEAD | `b2e129ab3f3bdf222f16040fcd0513872d0e2a3c` | +| C4 Dev HEAD | `308f33665eb2dfaf9ae661e87df8898c3c2ecb3a` | +| C5 Dev HEAD | `792574692609d9bfdf113e2dc5af6f43c9a176dd` | +| 主机 | 32 逻辑 CPU,122 GiB RAM;演练开始时 load average 见各 `host.txt` | +| 入口 | `tests/load/collaboration/run_pgbench.sh`;`tests/ops/run_fault_drill.sh`;`tests/ops/run_backup_drill.sh` | + +原始输出在测量 worktree 的 `results/`(gitignore),不入库。任务卡 evidence 为 SELF。 + +## 2. B6 pgbench + +结果目录 `tests/load/collaboration/results/pgbench-20261012-131948`。隔离实例 `u7bench`,scale 100,每档 60 秒,`-M prepared`。`max_connections=100`,`superuser_reserved_connections=3`,100 客户端封顶为 95。六档失败事务均为 0。 + +| 模式 | 客户端 | tps | P50 | P95 | P99 | +|---|---|---|---|---|---| +| select-only | 10 | 535646 | 0.018 ms | 0.024 ms | 0.030 ms | +| TPC-B 类 | 10 | 18798 | 0.339 ms | 0.673 ms | 5.560 ms | +| select-only | 30 | 952115 | 0.026 ms | 0.057 ms | 0.078 ms | +| TPC-B 类 | 30 | 52224 | 0.493 ms | 1.002 ms | 1.777 ms | +| select-only | 95 | 904708 | 0.087 ms | 0.180 ms | 0.417 ms | +| TPC-B 类 | 95 | 50607 | 1.041 ms | 3.361 ms | 18.106 ms | + +主机不空闲。这是数据库参考基线,没有业务阈值,也不替代容量窗口。 + +## 3. C4 断连 + +结果目录 `tests/ops/results/fault-20261012-142734`。PG 18991,应用 18990/18992,Redis 18993。 + +| 判据 | pg-stop | pg-kill | +|---|---|---| +| F1 有界失败、进程仍在 | 过 | 过 | +| F2 不回落 SQLite,新进程拒绝就绪 | 过 | 过 | +| F3 恢复后两侧各 5 次写入 | 过,RTO 不到 1 秒,未重启应用 | 同左 | +| F4 失败 requestId 重放恰好一条 | 过 | 过 | +| F5 dup_seq=0、seq 无洞、无非终态 run | 过 | 过 | + +Redis 限额 20 次/分钟,两实例各突发 40 个读: + +| 判据 | 结果 | +|---|---| +| G1 共享限额 | 放行 20、429 为 60、其他错误 0 | +| G2 断连 | 放行 40、429 为 40、其他错误 0;已确认写入仍在 | +| G3 只拉起 Redis | 放行回到 20;`rl:*` 均有过期时间 | +| G4 | 发现:日志看不到降级计数 | +| G5 | 发现:Redis 不通时新进程拒绝启动 | + +`runtime.redisAddr` 没有调用者可写的产品入口。演练用 SQL 写入默认 owner `local` 的 `user` 设置,并保留 mock。 + +## 4. C5 备份与恢复 + +结果目录 `tests/ops/results/backup-20261012-153441`。源 PG 18994,恢复目标 18997。合同数据量 seed 约 12 秒。 + +| 判据 | 结果 | +|---|---| +| K1 备份有效 | 过。`failed_count=0`,`archived_count=80`,`pg_verifybackup` 与 `pg_restore --list` 退出码 0 | +| K2 RPO≤5 分钟 | 过。64 秒 | +| K3 RTO≤60 分钟 | 过。4 秒。恢复后登录、列项目、读会话均为 200 | +| K4 工件与权限 | **FAIL**。五次上传都没有拿到工件编号 | +| K5 不变量 | 过。dup_seq=0,seq 无洞;恢复后消息/事件/知识/run 计数与故障前一致(消息 1 000 120);非终态 run 为 0 | +| K6 逻辑恢复 | 过。`pg_restore` 退出码 0,约 4 秒;计数等于 dump 时快照(消息 1 000 000,知识 100 000) | +| K7 权限与泄密 | 过。备份目录 700、文件 600;结果文本里没有连接串。静态加密与凭据演练记 N/A。归档和数据在同一块盘,没有模拟存储全毁 | + +恢复后的计数与故障前一致,说明已提交的标记都在。64 秒是最后一条成功标记的确认时间到故障时刻的间隔;窗口末尾有的写入因为会话仍被 mock run 占用而没有落库。 + +K4 的原因在源应用日志:未设置 `BLOBSTORE_S3_ENDPOINT` 时,`NewS3FromEnv` 返回空的 `*S3`。这个空指针被放进 `blobstore.Store` 接口后不再等于 nil,`finishOpen` 不会回落本地目录,上传在 `blobstore.(*S3).Put` 空指针 panic。产品代码未改。因此工件 sha256 和跨用户拒绝没有测成。代码里本地后端的 `content_path` 是绝对路径;这次没有成功上传,所以没有用恢复后的下载来核验。 + +同日另一次恢复(`backup-20261012-150713`)在数据目录复制之后起不来,因为 socket 路径超过 107 字节。正式这次把 socket 放到 `/tmp/u7bk-18997`。 + +## 5. 放行 + +B6、C4 为 SELF 通过。C5 的库恢复为 SELF 通过,工件恢复失败。第二实例写流量仍不放行。 + +还没做、本任务不启动的两项: + +- 独立 REPLAY,或把 C4/C5 的快速档接进 CI 成为 GATE(需要平台准入)。 +- C3 双主机。 +- 上传在未配置对象存储时 panic,需要单独的产品修复后才能重测 K4。