6.7 KiB
决策提案:运营航班表 FLIGHT_STATE 是否落自有 PostgreSQL
状态:提案(讨论 issue:ACM2-28,关联 ACM2-12)。 本文重估 ACM2-12「阶段 B 缓做、不落表」的口径;定案前不改代码与迁移。 交叉引用:architecture.md §6/§7、design.md §2/§3.4、 ACM2-12(存储边界)、ACM2-10 U05/U09/U15。
1. 问题
阶段 A 航班动态权威 = Redis(flightInfo hash + SCHD_GEN),永续驻留、不落任何关系表
(ACM2-12)。FLIGHT_STATE 表原属阶段 B(Decision.kt 注释即「阶段 B 落 FLIGHT_STATE
同事务」)。要回答的问题:是否把 FLIGHT_STATE 提前到阶段 A 落自有 PG,作为运营航班的
权威存储。
2. 触发重估的事实
| # | 事实 | 出处 |
|---|---|---|
| F1 | Redis 全损后,FLOP 报文按 KEEP 语义「航班不存在 → SUCCEEDED 不重试」持续终结——空态被当正常态,状态损坏持续到下一次完整 DNLD;且这些报文已回填 DATE_PROCESSED,ReplayService 白名单(FAILED/DEAD)无法找回 |
US-05 AC3、design.md §1.1 |
| F2 | 当前唯一恢复手段 = 等下一次 DNLD 或人工触发 RQFD(依赖 AODB 外部响应,超时 60s);无本地 durable 副本 | US-08、design.md §3.4 |
| F3 | dev Valkey 虽配 AOF(compose.yaml --appendonly yes),生产 Redis 拓扑(持久化策略/副本)未定;Redis 持久化本就是尽力而为,不构成状态安全边界 |
compose.yaml、§8 部署姿态 |
| F4 | PG 已是处理管道硬依赖(PROC_STATE/MSG_EVENT),主泵本就停摆于 PG 不可用——航班状态放 PG 不新增 系统级 SPOF | design.md §2 |
| F5 | U05(PG 本地事务边界)正在实装;此刻调整事务模型成本最低,切流后迁移要重开 I2/I5 与影子对拍口径 | ACM2-10 U05 |
| F6 | U09(gen Redis 内 CAS 协议重设计)存在的根因就是「权威在 Redis、终态在 PG」的跨存储窗口——ACMA-8 v4 原设计 gen 本在 DB,ACM2-12 迁 Redis 仅因 FLIGHT_STATE 缓做 | design.md §3.4 已知缺口 |
| F7 | Handler 输入视图 hgetAllFlightInfo() 每报文全量读 hash,已是 U22–U24 已知缺口;PG 按 FLID 索引读可顺带收敛 |
design.md §9 |
3. 选项
A — 维持缓做 + 运维缓解(ACM2-12 现状)
冷启动空态自动触发 RQFD、生产 Redis 强制 AOF+副本部署要求、Runbook 记录重建流程。 局限:F1 的永久损坏窗口依旧存在,缓解只是缩短;恢复依赖外部系统可用性。
B — PG 镜像(write-behind 灾备副本,非权威)
主泵在 Redis 写成功后异步把变更镜像到 PG,仅供灾难重建。 局限:镜像滞后窗口内的 FLOP 效果同样丢失,恢复后仍需 DNLD 修正(与 A 等价的损伤面); 却要付出接近 C 的复杂度(表 + 双表示一致性哨兵 + 重建任务)。性价比最差,仅列出备选。
C — FLIGHT_STATE 落自有 PG,作为阶段 A 权威(推荐)
航班状态变更并入既有 PG 事务 2(与 MSG_EVENT 插入、PROC_STATE→SUCCEEDED 同事务
原子提交);Redis 退出动态权威写路径。
- 跨存储双写窗口(I2 的 Redis 先写)整体消失;U09 从「Redis Lua 协议重设计」变为 「SQL 版本 CAS + 集成测试」,gen/SCHD_GEN 随 FLIGHT_STATE 回 PG(回到 ACMA-8 v4 形态)。
- Redis 全损场景不再存在:PG 即状态,重启即恢复;F1 的损坏路径被根除。
- Handler 输入改为按 FLID 索引读(F7);US-12
GET /all/flights读 PG。 - 影子对拍沿用 ACM2-12 既有口径(自有 PG 独立 schema 比对),不新增负担。
- 阶段 B 不受影响:清场/判史改为 SQL 驱动,ES 仍是历史投影。
- 性能:机场报文量级(秒级峰值)下单行 JSONB upsert 亚毫秒,且在既有事务内,无新增往返。
C 的代价(须如实计入):
- 推翻 ACM2-12 阶段 A 存储口径,I2/I5 不变量重述,U29 不变量测试清单同步。
FlightStateRepository从 day 粒度(replaceDay)重设计为 FLID 粒度 upsert + day 快照 replace;SnapshotFlow 的 Lua 差删改 SQL;redis-flight-store健康指示器调整。- Redis 在阶段 A 角色大幅缩小(仅剩 orms_stand 热点缓存可选),部署面与文档需收口。
- 与 U05/U09/U15 排序重排:先定此决策,再收 U05 事务边界。
4. 对比
| 维度 | A 缓做+缓解 | B 镜像 | C PG 权威 |
|---|---|---|---|
| Redis 全损后果 | 状态永久损坏至下次 DNLD | 损坏窗口缩短,仍需 DNLD 修正 | 不存在该场景 |
| 事务模型 | 跨存储双写(I2/U09 复杂度保留) | 双写 + 镜像一致性 | 单库原子(U09 消解为 SQL) |
| 新增范围 | 无 | 表+哨兵+重建 | 表+接口+事务改造(并入 U05/U09 批次) |
| 影子对拍 | 不变 | 需比对三方 | 不变(口径已是 PG schema 比对) |
| 恢复 RTO/RPO | 依赖 AODB 响应 | 本地副本+DNLD 修正 | 重启即恢复,RPO=0 |
5. 建议
推荐 C。核心理由:F1 的损坏是永久且不可自动找回的(报文已回填、不可重放), 选项 A/B 只能缩短窗口不能根除;而 C 的增量成本大部分落在 U05/U09 本就要动的事务边界上, 时机(F5)与代价重合。若评审认为生产 Redis 必然配强持久化+副本且接受 DNLD 重建窗口, A 是最小代价回退位;B 不建议。
6. 定案前置问题(→ Plane issue 讨论)
- 生产 Redis/Valkey 拓扑定案:AOF 策略、副本、RPO 承诺——决定选项 A 是否可接受。
- FLIGHT_STATE 形态:FLID 主键 + FLTR JSONB 全量(建议起步),还是归一化列
(
abdg/PSDT等派生字段是否有 SQL 查询面需求)。 - gen/SCHD_GEN 回 PG 后,U09「Redis Lua 协议重设计」是否直接取消,改为 SQL 版本 CAS
- Testcontainers 集成测试。
- 阶段 A 是否彻底移除 Redis 依赖:orms_stand 缓存去留、US-12 读取面、
redis-flight-store健康指示器调整。 - I2/I5 不变量重述文案与 U29 清单、architecture/design/user-stories 三文档的回改范围。
- 影子对拍跨存储 diff(nextgen PG vs legacy Redis)的验收口径与工具。
7. 定案后的落地清单(C 方案)
- 迁移
V1.1.0__flight_state.sql:FLIGHT_STATE + gen 列回归(含索引 FLID/日期)。 FlightStateRepository重设计(FLID upsert / day replace / findByFlid)+ JDBC 实装。MessageProcessor/SnapshotFlow:redisApply → PG 事务内;Lua 差删 → SQL。- Handler 视图与 US-12 读 PG;健康指示器、影子隔离(Redis key 前缀条目作废)同步。
- 文档回改:本文转「已定案」,ACM2-12 阶段 A 口径修订说明。