feat(education): establish operational independence contracts

This commit is contained in:
2026-07-31 12:54:08 +08:00
parent 428d4e10fd
commit f42ef76527
24 changed files with 740 additions and 20 deletions

View File

@@ -13,18 +13,19 @@ yudao:
catalog-read-enabled: true
practice-write-enabled: true
pilot-tenant-ids: [<pilot-tenant-id>]
catalog-mode: SCALAR_READ
catalog-mode: JAVA_READ
scalar:
enabled: true
base-url: ${EDUCATION_SCALAR_BASE_URL}
token: ${EDUCATION_SCALAR_TOKEN}
enabled: false
owner: <required-only-when-scalar-read>
exit-date: <yyyy-MM-dd>
```
要求:
- `pilot-tenant-ids` 在 Pilot 环境必须显式配置,不能使用空列表。
- Scalar token 只能通过密钥管理或环境变量注入,不写入仓库、日志或测试报告
- 发布前调用管理端 `/admin-api/education/capability`,核对模块、题库读取、练习写入和 Pilot 租户数量
- Pilot 默认以 `JAVA_READ` 启动,只依赖目标 PostgreSQL不得启动旧 NestJS API、旧 Worker、Supabase 或旧资产扫描服务作为前置条件
- 仅在有明确负责人、告警、故障策略和退出日期的兼容窗口内切换 `SCALAR_READ`。Scalar token 只能通过密钥管理或环境变量注入
- 发布前调用管理端 `/admin-api/education/capability``/admin-api/education/operations/health`;后者必须显示 `java-read-postgresql=UP``scalar-catalog=NOT_SELECTED`
## 3. 发布步骤
@@ -32,9 +33,10 @@ yudao:
2. 使用 Server 配置的 PostgreSQL Flyway 执行 migrate 和 validate检查版本、脚本、checksum 与 success不得手工应用 Education SQL 或执行 rollback SQL。
3. 先以 `catalog-read-enabled=false``practice-write-enabled=false` 部署应用。
4. 验证 System、Infra、Member 基础 smoke。
5. 仅对 Pilot 租户开启题库读取,完成 Scalar 只读 smoke
5. 仅对 Pilot 租户开启题库读取,完成 JAVA_READ 只读 smoke并运行 `tools/education-target-smoke/java-read-readiness.sh`
6. 对 Pilot 租户开启练习写入,完成会话、答案、交卷、报告、错题和收藏 smoke。
7. 观察错误率、延迟和数据库写入后再扩大租户列表
7. 若部署 Worker 或 Scanner先确认其持续写入 `education_operational_component`,且 `/admin-api/education/operations/health``DOWN` 组件和未处理死信
8. 观察错误率、延迟和数据库写入后再扩大租户列表。
## 4. Smoke 清单
@@ -60,7 +62,17 @@ yudao:
## 5. 故障与回滚
### Scalar 故障
### JAVA_READ / PostgreSQL 故障
1. 设置 `catalog-read-enabled=false`,停止新的目录和题目读取;不得静默切回 Scalar。
2. 保持 `enabled=true`,使已有会话、报告、错题和收藏仍可访问。
3. 检查 `/admin-api/education/operations/health``java-read-postgresql` 结果和 Flyway 历史。
4. 如需冻结新写入,再设置 `practice-write-enabled=false`
5. 通过应用回滚或更高版本 Flyway 前滚修复,不执行 `flyway clean` 或手工回滚 SQL。
### Scalar 兼容窗口故障
仅当部署明确选择 `SCALAR_READ` 时适用:
1. 设置 `catalog-read-enabled=false`,停止新的 Scalar 读取。
2. 保持 `enabled=true`,使已有会话、报告、错题和收藏仍可访问。
@@ -88,7 +100,10 @@ yudao:
发布窗口至少观察:
- Scalar 请求成功率、4xx/5xx/timeout、P95/P99 延迟
- `/admin-api/education/operations/health` 的必需依赖、Worker/Scanner 心跳和 open dead-letter 数
- `education_operational_component``last_heartbeat_at`、最后成功/失败和 backlog
- `education_dead_letter` 仅保留负载指纹与脱敏错误分类,不得保存业务 payload、凭据或个人数据
- Scalar 兼容模式请求成功率、4xx/5xx/timeout、P95/P99 延迟;
- 练习创建成功/冲突数;
- 答案保存成功、幂等重放、版本冲突和旧序号拒绝数;
- 交卷成功、并发冲突和事务失败数;
@@ -97,6 +112,14 @@ yudao:
Scalar 日志只能记录脱敏路径、tenant ID、上游 request ID、状态、耗时和错误分类不得记录 Authorization、Scalar token、学生答案、正确答案或完整响应体。RuoYi access/error log 中的 trace ID 用于关联入口请求;验收时需保存一条从入口日志到 Scalar request ID 的关联证据。
### Worker、Scanner 与死信处置
1. Worker/Scanner 每次心跳使用固定 `component_key` upsert部署实例变化写入 `instance_id`
2. 心跳状态只能为 `STARTING/UP/DEGRADED/DOWN``detail` 必须脱敏且有界。
3. 重试耗尽后写入 `education_dead_letter`同一租户、组件、workload 只允许一个 OPEN 记录。
4. 排障后先把原 OPEN 记录标记为 `REQUEUED` 并填写 `resolved_at`/`resolution_note`,再通过所属业务服务重入队;禁止直接修改业务结果或把原 payload 写入死信表。
5. 未部署对应 Worker/Scanner 时不得伪造 UP 心跳;能力清单应保持未交付状态。
## 7. 验证命令
```bash