Why: Second-instance writes stay closed until an independent replay or an admitted CI gate, plus a second host. Those remaining steps needed a durable spec. Evidence: Task-card evidence rows read the merged PRs, tree equality, CI runs 2783/2810/2816, and Dev main branch protection. Source SELF. Drills were not run. Impact: Docs and the task card only. Checklist grades stay SELF or not started. The collaboration proposal body stays r2.3. Refs: docs/release-multi-instance.md Co-authored-by: Cursor <cursoragent@cursor.com>
102 lines
4.9 KiB
Markdown
102 lines
4.9 KiB
Markdown
# U7 独立重放规格(未执行)
|
||
|
||
状态:规格。本文件不记录一次已经完成的重放,也不改变
|
||
[放行检查单](release-multi-instance.md) 的证据等级。第二实例写流量保持关闭。
|
||
|
||
2026-10-12 已决定不做容量重放。本规格只覆盖 C5、C4 和 A6,以及对象存储回落。
|
||
|
||
## 固定对象
|
||
|
||
从干净检出开始,物化子模块后再跑命令。默认对象是 Dev
|
||
`3fb8210f6495a78e70a67af3661bdfe9c00fb9de`,其 gitlink 为:
|
||
|
||
| 组件 | SHA |
|
||
|---|---|
|
||
| Backend | `dc736e339d01c59e658abc63c81909059bd6ea02` |
|
||
| Documents | `05c13a42b14d70f878cc8a51c543e018a7f7a663` |
|
||
|
||
合入提交与对应 PR head 在本地 `git rev-parse ^{tree}` 上同树。核验输出在任务卡
|
||
`.task/feature-u7-release-roadmap.md` 的证据表,来源 SELF。若重放日 main 已前进,
|
||
重放者改用新的完整 SHA,并重新读出 backend 与 documents 的 gitlink,写进重放记录。
|
||
|
||
## 重放者
|
||
|
||
重放者是另一个人,或另一台由该人操作的机器。先跑完命令并写出结论,再打开作者收据。
|
||
作者保全目录 `u7-k4-retest-preserved-20261013` 与其中的 `task-finish.txt` 属于作者证据。
|
||
|
||
同一账号下另开一个 agent 会话、或切换角色,不能记成 REPLAY。
|
||
`policy/platform-admission.md` 已写明不能把切换 Agent 角色当独立账号。
|
||
重放者身份未指定前,本规格不启动。
|
||
|
||
## 环境
|
||
|
||
- 只使用名称前缀 `u7drill` 或 `u7bk` 的实例,端口落在 18980–18999。
|
||
- 启动前确认环境里没有 `BLOBSTORE_S3_ENDPOINT`。演练脚本本身不设置该变量。
|
||
应用把 `NewS3FromEnv` 返回的空 `*S3` 放进 `blobstore.Store` 接口,
|
||
`blobstore.IsNil` 把它当成未配置,工件写入 `--artifacts` 目录。
|
||
- 已构造的对象存储由 `TestFinishOpenKeepsConfiguredBlobStore` 覆盖,重放时一并跑该测试。
|
||
本次不要求另起一台对象存储。
|
||
- `tests/ops/lib.sh` 的资源门禁要求数据目录所在盘可用空间至少 100GiB,且 1 分钟负载不超过 16。
|
||
不满足时脚本会停,不能改门禁来换一次通过。
|
||
|
||
## 最小重放集
|
||
|
||
按这个顺序执行。每条用 `CMD="<命令>" make evidence` 写入重放者自己的任务卡。
|
||
工具写入的来源标签仍是 SELF;审阅记录里另行声明 REPLAY,并写明重放者、主机、命令和对象 SHA。
|
||
|
||
1. `tests/ops/self_check.sh`
|
||
2. `tests/ops/run_backup_drill.sh`
|
||
3. `tests/ops/run_fault_drill.sh --fault all`
|
||
4. 用隔离的 PostgreSQL 跑 `make test-multi-instance`(见
|
||
`tests/integration/multi-instance/README.md`)。这套库使用文档中的示例端口时,
|
||
不要占用演练的 18980–18999。
|
||
5. 在 `modules/backend` 执行:
|
||
|
||
```bash
|
||
go test -count=1 -timeout 15m ./internal/blobstore/ ./internal/store/ \
|
||
-run 'TestIsNil|TestNewS3FromEnvFallback|TestFinishOpenTypedNilS3UsesLocalArtifacts|TestFinishOpenKeepsConfiguredBlobStore|TestOpenPostgresTypedNilS3UsesLocalArtifacts'
|
||
```
|
||
|
||
`TestOpenPostgresTypedNilS3UsesLocalArtifacts` 需要真实 PostgreSQL。没有 DSN 时该项记
|
||
NOT_RUN,其余四项仍要跑。
|
||
|
||
## 判据
|
||
|
||
C5 以 `tests/ops/run_backup_drill.sh` 写出的 `verdict.json` 为准:
|
||
|
||
| 项 | 通过条件 |
|
||
|---|---|
|
||
| K1 | 归档成功且 `failed_count` 为 0,基础备份与逻辑备份清单校验通过 |
|
||
| K2 | RPO 在 0 到 300 秒之间 |
|
||
| K3 | 恢复后登录、项目、会话均为 200,且 RTO 不超过 3600 秒 |
|
||
| K4 | 工件 `checked > 0`、`missing = 0`、`mismatch = 0`,跨用户访问项目和工件均为拒绝 |
|
||
| K5 | `dup_seq = 0`、`seq_gap_convs = 0`,恢复后的行数落在备份点与故障点之间 |
|
||
| K6 | 逻辑恢复的行数与备份点一致 |
|
||
| K7 | 备份文件权限收紧,结果目录无凭据泄漏 |
|
||
|
||
C4 以故障脚本的 F1–F5 与 Redis 轨 G1–G3 为准。G4 降级不可见、G5 启动时 Redis 不通则拒绝,
|
||
沿用检查单里的「发现」,不把它们改成产品失败。
|
||
|
||
A6 以 `make test-multi-instance` 退出码 0 为准。
|
||
|
||
K4 还要看应用日志里没有 `blobstore.(*S3).Put` 的空指针 panic,并且工件文件出现在该次
|
||
`--artifacts` 目录。脚本只在拿不到工件 id 时才写 `finding-upload.txt`;那个文件里的
|
||
panic 句子是脚本自带的说明,重放时以应用日志和 `artifacts-verify.json` 为准。
|
||
|
||
与 2026-10-13 SELF 目录 `backup-20261013-013354` 对照时,比较的是上述判据。
|
||
RPO、RTO 的秒数可以不同。判据不一致则阻塞,并按失实登记。
|
||
|
||
## 重放记录模板
|
||
|
||
重放完成后把记录放到 Documents 的 `docs/records/`,并回写检查单的 C4、C5、A6 证据等级。
|
||
那一步另需授权。记录至少包含:
|
||
|
||
- 重放者与主机(人,或人操作的另一台机器)
|
||
- 开始前读取的 Dev、Backend、Documents 完整 SHA
|
||
- 每条命令、退出码、结果目录名
|
||
- `verdict.json` 里的 K1–K7、`rpo_s`、`rto_s`
|
||
- C4 的 F1–F5 与 G1–G5
|
||
- A6 退出码
|
||
- 五项单测的通过或 NOT_RUN
|
||
- 来源声明:审阅记录为 REPLAY;`make evidence` 行仍为 SELF
|