feat(education): establish operational independence contracts
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# EDU-015 — Operational independence and legacy exit
|
||||
|
||||
- **Status:** blocked
|
||||
- **Status:** implemented-contracts
|
||||
- **Type:** integration and deployment program
|
||||
- **Phase:** 6
|
||||
- **Blockers:** EDU-011, EDU-013, EDU-014 child decisions, all temporary-adapter owners and exit plans
|
||||
- **Blockers:** production deployment evidence and implementation of each future EDU-011 worker/scanner workload
|
||||
|
||||
## Outcome
|
||||
|
||||
@@ -18,6 +18,17 @@ The target backend runs its selected education capabilities without depending on
|
||||
- Reconcile migrated data and operational runbooks.
|
||||
- Prove deployment, startup, Flyway, and core user flows.
|
||||
|
||||
## Delivered contracts
|
||||
|
||||
- `/admin-api/education/operations/health` exposes the runtime dependency registry plus durable Worker/Scanner and open dead-letter summaries.
|
||||
- `JAVA_READ` performs a target-only PostgreSQL schema readiness proof; Scalar is explicitly `NOT_SELECTED` in that mode.
|
||||
- `SCALAR_READ` is marked legacy, required only when selected, uses `FAIL_CLOSED`, and records success/failure telemetry without exposing URL or credentials.
|
||||
- V4170 owns durable `education_operational_component` and tenant-scoped `education_dead_letter` contracts without retaining business payloads.
|
||||
- `EducationTenantContextPropagation` re-establishes and restores tenant context for executor tasks.
|
||||
- `tools/education-target-smoke/java-read-readiness.sh` asserts target-only provider selection against a running target deployment.
|
||||
|
||||
Production migration and deployment evidence remain release activities and are not claimed by this change.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Every temporary legacy dependency has an owner, telemetry, failure policy, and exit date.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user