Files
hms/docs/discussions/2026-06-25-analysis/01-stability.md
iven 3351c68d10 docs: redact Redis 凭据明文 + 系统分析报告 + wiki 关键数字校正
PP-03 凭据泄露处置:
- 清除 wiki + 2 份历史文档中的 Redis 明文密码与公网 IP(4 文件 5 处)
- wiki 新增安全告警 + 症状导航条目
- 核实降级:泄露旧密码已失效,HMS 连本地 Redis,云端闲置;公网已关闭

系统深度分析(9 维度 + 6 主题多专家组):
- docs/discussions/2026-06-25-analysis/ 新增 7 文件
- 综合 6.8/10,4 CRITICAL,TOP 12 痛点,4 阶段路线图

wiki 关键数字校正(PP-02/05a fix 触发):
- 迁移数 175→176(m20260626_000170)
- 症状导航新增 device_readings 分区硬截止 + claim_next 注入修复条目
2026-06-26 09:07:35 +08:00

126 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 稳定性与上线护航 — 主题综合
> 日期: 2026-06-25 | 主题: 稳定性与上线护航(主持综合)
> 视野: V1 上线后 6-12 个月演进,不重复上线前就绪度讨论。
> 证据口径: 所有论断附 `文件:行号`,基于 feat/media-library-banner 分支实测。
## 1. 主题愿景
把"上线即救火"变成"上线即睡觉"。融合 SRE / 发布管理 / 质量保障三方共识:以**确定性故障自愈**为核心死信重试、分区到期、AI 队列积压——这三类都是"代码写了但没接线"或"硬截止定时炸弹",是历史 24% fix 提交率在事故层面的根因),以**迁移可逆性 + 快速回滚**为发布纪律,以**告警触达人 + cron_heartbeat 进就绪门禁**为观测闭环。三者形成"预防—发布—响应"完整生命周期,而非堆砌监控工具。
不追求一步到位上 K8s+Helm+完整可观测性栈DevOps 4.2 分、单兵运维团队不支撑这种复杂度债务)。最小可行 HA = migrate 子命令 + 蓝绿 + heartbeat 门禁 + Alertmanager每一步都是可验证的工程动作配合 TDD 集成测试证明"接线真的生效"。
## 2. 专家提案摘要
### SRE 可靠性工程师5 项)
- **PP-04 告警触达人**:补 Alertmanager + 三级 SEV 分级 + Runbook deep-link + webhook 限流治理。
- **PP-01/PP-05 死信与 AI 队列接线**`retry_dead_letters`(events.rs:382) 和 `analysis_queue::claim_next`(service/analysis_queue.rs:92) 已实现但脱节,接线 + cron_heartbeat 复用为后台存活探针。
- **PP-02 分区自愈**:应急 guard每日检测未来分区数<2 则补建)+ pg_partman 根治,剩余周数作 SEV-1 倒计时告警。
- **灾备演练制度化**restore-drill CI job → RPO/RTO Prometheus 指标。
- **PP-11 迁移解耦启动路径**migrate 子命令 + 蓝绿 + 破坏性 DDL 三步走。
### 发布管理专家5 项)
- **迁移可逆性工程化**clap 子命令暴露 173 条已存在但休眠的 down 迁移为可调用能力expand/contract 强制破坏性变更跨版本。
- **30 分钟回滚**`migrate down <ver>` + 双标签镜像 + canary 用 `eventbus_pending_total`(tasks.rs:119) 作健康判据。
- **cron 进就绪门禁**`/health/ready`(health.rs:51) 暴露 cron_heartbeat + 积压阈值503 触发摘流。
- **系统级特性开关**FeatureFlagServiceai 内部)上提为 erp-core trait按 tenant 粒度灰度。
- **冻结期 + fire-drill**:双周演练 + 兼容窗口契约沉淀到 docs/runbooks/。
### 质量保障架构师(占位,观点合并入下)
门禁反推视角已贯穿上述——TDD 集成测试证明接线生效、CI 门禁拦截破坏性 DDL、fire-drill 验证回滚链路,本主题不再单列。
## 3. 战略举措(归并后 5 项)
### 举措 A确定性故障自愈接线PP-01 + PP-05 + PP-02 应急)
- **rationale**retry_dead_letters 已实现 (events.rs:382-446含 max_attempts=5 filter events.rs:390) 但全仓无调用者ai_analysis_queue 表已建 (m000118) 且 claim_next 已实现 (analysis_queue.rs:92) 但只在 module 启动触发一次device_readings 是分区表down() 只 DROP 固定 4 个月份 (2026_05..2026_08)2026-09 后 INSERT 将硬失败。三处都是"代码写了但没接线"的定时炸弹。
- **phases**:
1. tasks.rs 新增 `start_dead_letter_retry`(每小时调 retry_dead_letters复用 cron_heartbeat+ `start_ai_queue_worker`(消费 ai_analysis_queue
2. tasks.rs 新增 `start_device_readings_partition_guard`(每日检测 `pg_inherits` 未来分区数<2 则 CREATE PARTITION OF
3. TDD先写 `tests/event_retry_loop.rs``tests/partition_guard.rs` 失败测试,再接线。
- **effortEstimate**3-5 人日(函数均已存在,主要是接线 + 测试)。
- **expectedImpact**:消除 3 类潜伏故障死信不再永久驻留2026-09 分区硬截止提前自愈。
- **kpis**dead_letter_events.resolved_at 非空率 >95%ai_analysis_queue pending 24h 内清零device_readings 未来分区数 ≥3。
- **dependencies**cron_heartbeat metric 暴露(举措 CAlertmanager 告警规则(举措 D
### 举措 B迁移解耦启动路径 + 30 分钟回滚能力
- **rationale**main.rs:233 在应用启动路径直接 `Migrator::up`,含 RENAME/DROP 等破坏性 DDL173 条 down 迁移已写好但全仓无 clap/migrate 子命令可调用Cargo.toml 无 clap 依赖docker-compose.production.yml:38 单容器 `hms-server`nginx.conf:1 单 upstream.github/workflows 仅 test.yml 无构建推送。回滚资产存在但休眠。
- **phases**:
1. 引入 clap 子命令:`erp-server serve`(仅启动 HTTP不迁移/ `migrate up|down <ver> --dry-run --confirm` / `migrate verify`(事务回滚校验 down 可逆性)。
2. main.rs:233 改为启动时仅校验 schema 版本一致性,不匹配则 panic 提示先跑 migrate。
3. CI 新增 build-and-push workflow双标签 `{git-sha}` + `{semver}` 推 ghcr.io。
4. deploy.shcanary 10% 流量,观察 `/metrics` 5xx + eventbus_pending_total 5 分钟,异常切回 stable + migrate down。
5. 破坏性 DDL 写入 docs/runbooks/breaking-ddl.md 强制 expand/contract 三步走 checklist。
- **effortEstimate**8-12 人日clap 改造 + CI + deploy 脚本 + 破坏性迁移 checklist
- **expectedImpact**:回滚从"重拉镜像"升级为 30 分钟内可执行;破坏性 DDL 不再在启动瞬间执行。
- **kpis**MTTR <30min破坏性迁移 100% 走 expand/contract任意历史镜像可拉取。
- **dependencies**backup.sh 作为回滚前快照已存在nginx upstream 改造(举措 B 第 4 步)。
### 举措 Ccron_heartbeat 进就绪门禁 + 积压指标可观测
- **rationale**cron_heartbeat 已埋点 (main.rs:647 → state.rs:31 → tasks.rs 已写),但 readiness_check (health.rs:51) 仅查 DB+Redis后台任务死了就绪仍返回 oktasks.rs:119 已暴露 eventbus_pending_total但 dead_letter 积压、AI 队列积压、分区剩余周数未导出。零成本资产未利用。
- **phases**:
1. ReadyResponse (health.rs:33) 增 `crons: [{name, last_heartbeat_ago_secs, healthy}]`>2×周期判 unhealthy 返回 503。
2. tasks.rs 新增 gauge`dead_letter_unresolved_total``ai_analysis_queue_pending_total``device_readings_partitions_remaining_weeks`
3. 公开路由屏蔽 crons 字段防信息泄漏。
- **effortEstimate**2-3 人日。
- **expectedImpact**:后台任务死亡可被 nginx/k8s 摘流canary 阶段可直接判断新版本是否杀掉某个 cron。
- **kpis**/health/ready 反映 cron 存活canary 拒绝率(后台被杀)可观测。
- **dependencies**:举措 A 接线后 cron 才有真实心跳。
### 举措 DAlertmanager + 三级告警 + Runbook 绑定
- **rationale**prometheus.yml:5 仅有 rule_files 无 alerting 块alerts.yml 22 条规则但无 SEV 分级、无 Alertmanager、无 Runbook 链接。告警亮了无人知、知了不知干啥。
- **phases**:
1. docker-compose.production.yml 新增 alertmanager 服务 + prometheus.yml 补 `alerting: alertmanagers:` 段。
2. alerts.yml 按 SEV-1/2/3 分级SEV-15xx 率/PG 耗尽/Redis 不可达/dead_letter 积压/分区<2 周 → 企微+电话SEV-2P95/idle<10% → 企微SEV-3CPU/内存 → 仅 Grafana
3. 每条 alertname 对应 docs/runbooks/ 一页 + Grafana deep-link。
4. 上线初期只开 SEV-1按周复盘降噪后再开 SEV-2/3信噪比治理
- **effortEstimate**4-6 人日。
- **expectedImpact**:值班人被电话叫醒 ≤1 次/天;确定性故障(分区到期、死信积压)提前 2 周报警而非等客户投诉。
- **kpis**:告警信噪比(有效告警/总告警)>70%SEV-1 平均响应 <15min。
- **dependencies**:举措 A/C 的 metric 导出;企微 webhook 限流20/min评估。
### 举措 E灾备演练制度化 + 兼容窗口契约
- **rationale**backup.sh/restore.sh 已实现AES-256-CBC但从未验证可恢复无 fire-drill无兼容窗口契约文档。医疗 SaaS 业内普遍"有 backup 没 drill"。
- **phases**:
1. docker/drill/restore-drill.yml独立 PG 副本CI 周日 04:00 拉最近 backup → restore → schema diff + 10 条 smoke 查询 → 输出 RPO/RTO metric。
2. docs/runbooks/disaster-recovery.md + release.md + rollback.md + partition-deadline.md。
3. wiki/architecture.md 新增"发布兼容性"章节 + PR 标注 `compatible_rollback_to`
4. 双周 staging fire-drill + 变更冻结期(月初高峰前 48h
- **effortEstimate**6-8 人日(含 CI 隔离 + PII 合规审查)。
- **expectedImpact**RPO/RTO 从纸面 SLA 变成每周可查 metric客户合规审计直接拿数据。
- **kpis**backup_rpo_seconds / backup_rto_seconds 周报fire-drill MTTR 记录。
- **dependencies**CI runner 隔离(生产备份含 PIIBACKUP_PASSPHRASE 轮换流程。
## 4. 速赢1-2 周内)
1. **cron_heartbeat 进 /health/ready举措 C 第 1 步)**health.rs:33/51 改造零依赖、1-2 人日,立即可让 nginx 摘流反映后台存活。这是所有后续观测的门禁基线。
2. **死信与 AI 队列接线(举措 A 第 1 步)**tasks.rs 加两个 spawn + 复用 retry_dead_letters/claim_next3-4 人日,消除"代码写了没跑"的潜伏故障,并 TDD 证明生效。
3. **Redis 凭据止血轮换PP-03 应急)**:立即改 .env.production 注入新密码 + 重建 Redis 数据,不动 git 历史filter-repo 留待下一正常发布窗口。1 人日,止血无破坏性。
## 5. 主题级风险
- **R1 破坏性 DDL 三步走拉长交付周期 2-3 倍**:产品/研发强烈反对,需明确医疗场景牺牲速度换可靠性的边界,并以"特性开关(举措未单列)作廉价回滚"对冲。
- **R2 重试风暴**retry_dead_letters 广播后消费者仍失败会再次 dead-letter已确认 max_attempts=5 被 filterevents.rs:390兜底但 AI queue worker 启动后历史积压一次性触发大量 LLM 调用 → Ollama OOM需先 truncate 或加 backpressure。
- **R3 蓝绿双副本 +30% 资源成本**需与运维预算对齐nginx upstream 切换需 DB schema 兼容期,双写易引入数据不一致。
- **R4 BACKUP_PASSPHRASE 轮换需重加密历史备份**工作量被低估restore-drill 跑生产备份含 PIICI runner 必须隔离 + 跑完即销毁。
- **R5 告警信噪比未治理重蹈"狼来了"**:上线初期只开 SEV-1企微 webhook 限流 20/min 需评估,否则告警风暴被腾讯截断。
- **R6 /health/ready 返回 503 过敏感致发布期误摘流**:发布期临时调大阈值或加 maintenance 模式。
- **R7 pg_partman 需 superuser + shared_preload_libraries**:云 PG腾讯云可能限制私有化部署客户需同步安装扩展。
## 6. 专家分歧调和dissentingViews → 最终取舍)
- **D1 PP-03 Redis 凭据处置优先级**(安全 vs SRE vs 发布三方分歧SRE 主张业务链路自愈优先;安全主张立即 filter-repo 清洗历史;发布主张 filter-repo 是破坏性"发布"本身会重写哈希破坏分支。**取舍**:采纳发布管理专家的两阶段方案——立即轮换密码止血(无 git 影响)+ 下一正常发布窗口做 filter-repo带备份+全员协调)。理由:上线临门一脚搞历史重写风险高于泄露被利用的渐进风险,止血优先。
- **D2 是否上 K8s+Helm**SRE 明确反对,主张蓝绿+migrate 子命令是最小可行 HA。**取舍**:采纳 SRE 立场,不上 K8s。理由DevOps 4.2 分、单兵运维团队把复杂度债务换成事故概率不划算;蓝绿+heartbeat 门禁覆盖 80% 发布安全需求。
- **D3 可观测性栈建设时机**DevOps 专家 vs 发布管理边界之争):发布管理主张先 heartbeat+积压指标覆盖发布决策1 周可落地DevOps 主张上完整 Alertmanager/Loki/Jaeger4-8 周)。**取舍**:两者互补但分期——先落地举措 Cheartbeat 门禁)和举措 D 第 1-2 步Alertmanager + SEV-1完整链路追踪Loki/Jaeger列入 V1.1 路线图而非 V1 上线阻塞项。
- **D4 分区告警方式**SRE vs 监控专家SRE 主张按时间倒计时 metric 告警,监控专家常规按错误率。**取舍**:采纳 SRE——确定性故障硬截止不该按概率监控按剩余周数单调递减 gauge 提前 10 周预警。
- **D5 特性开关是否上提 erp-core**:违反 CLAUDE.md §1.3 模块边界铁律。**取舍**:以 trait 形式定义在 erp-core各模块可选依赖非直接耦合开关生命周期规范——最多存活 2 个发布周期后强制移除,防 if-flag 蔓延。本主题未单列举措,并入举措 B 回滚工具箱。
## 7. 路线6-12 个月)
- **M0上线前/上线时1-2 周)**:速赢 1+2+3 —— heartbeat 门禁、死信/AI 队列接线、Redis 凭据止血。
- **M1上线后 1 个月)**:举措 B 第 1-3 步clap 子命令 + CI 镜像推送)+ 举措 D 第 1-2 步Alertmanager + SEV-1+ 举措 A 第 2 步(分区 guard 应急)。
- **M2上线后 2-3 个月)**:举措 B 第 4-5 步canary + 破坏性 DDL checklist+ 举措 E 第 1-2 步restore-drill + runbooks+ pg_partman 根治。
- **M3上线后 4-6 个月)**:举措 E 第 3-4 步(兼容窗口契约 + fire-drill 制度化)+ 完整可观测性栈Loki/Jaeger评估。
- **M46-12 个月)**:根据 M1-M3 的 MTTR/RPO/RTO 实测数据,评估是否升级到 K8s+Helm届时团队规模与 DevOps 成熟度可能已支撑)。
> 所有举措均强调 TDD 证明接线生效、CI 门禁拦截、fire-drill 验证——质量保障"门禁反推"视角贯穿全程,不单列质量举措。