docs: remove retired schedule generation design

This commit is contained in:
windyboy
2026-09-09 20:22:10 +08:00
parent f6f9d6c6cb
commit d53a0a1c2f
8 changed files with 106 additions and 481 deletions
+3 -183
View File
@@ -1,185 +1,5 @@
# 历史存档说明
# 历史设计存档说明
以下是整理前的决策原文,包含已撤销方案和当时的交付声明;现行结论见 [决策摘要](../decision-flight-state.md)
旧版航班状态方案已撤销,不再作为设计或实现依据
# 决策:运营航班表 FLIGHT_SCHD 落自有 PostgreSQL 作阶段 A 权威(已定案 · 采纳选项 C)
> **状态**:已定案(讨论 issue**ACM2-28**,采纳选项 C;关联修订 ACM2-12)。
> 本文正式确立选项 C 为最终方案:运营航班权威 `FLIGHT_SCHD` 与代版本 `SCHD_GEN` 提前至阶段 A 落自有 PostgreSQL;Redis 彻底退出动态权威与写路径。
> 交叉引用:[architecture.md](../architecture.md)、[design.md](../design.md)、[user-stories.md](../user-stories.md)、ACM2-10FS1FS8 实施批次)。
> **2026-09-08 复核入口**:详细证据与重构提案见 [运营航班状态设计评审](../flight-state.md)。
> 本轮确认的部署边界:现场提供 Oracle 时使用 Oracle 11g,否则自行部署 PostgreSQL。
> 评审保留单库原子提交方向,但发现 §9 固定资源槽位与本仓库三登机门样例冲突,
> 并指出集合往返、迁移与快照 CAS 的风险。**§9 全宽表·零子表方案已按 v2 设计撤销**,
> 现行权威口径见 [§10 v2 落地](#10-acm2-29-v2-无损明细表方案落地2026-09-08)、
> 现行设计见 [flight-state.md](../flight-state.md);§1–§9 保留为历史记录。
## 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 | U09gen Redis 内 CAS 协议重设计)存在的根因就是「权威在 Redis、终态在 PG」的跨存储窗口——ACMA-8 v4 原设计 gen 本在 DBACM2-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 快照
replaceSnapshotFlow 的 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. 定案结论(ACM2-28 决策记录)
| # | 问题 | 定案结论 |
|---|---|---|
| Q1 | 生产 Redis 拓扑 / RPO | **不再阻塞**。Redis 退出权威与写路径,不构成状态安全边界。 |
| Q2 | 表形态 | 表名定案 `FLIGHT_SCHD`。FLID 主键 + FLTR JSONB 全量 + FDAY 所属代列(可空)。派生字段后置。 |
| Q3 | U09 重定义 | 「Redis Lua 协议重设计」**取消**,改为「`SCHD_GEN` SQL 版本 CAS + Testcontainers PG 集成测试」。 |
| Q4 | Redis 去留 | 阶段 A 动态写路径归零;orms_stand 缓存不保留;US-12 读 PG`redis-flight-store` 健康指示器移除。 |
| Q5 | 不变量重述 | I2(变更+事件+SUCCEEDED 单事务提交);I4(覆盖+差删+版本推进单事务);I5(单写者主泵线程,PG advisory lock 保护)。 |
| Q6 | 影子对拍口径 | nextgen PG `FLIGHT_SCHD` vs legacy Redis `flightInfo` 跨存储 AST 递归比对(FS7 工具)。 |
## 7. 落地完成清单(FS1–FS8 全部交付)
- **FS1** 迁移 `V1.1.0__flight_schd.sql`:创建 `FLIGHT_SCHD``SCHD_GEN`TIMESTAMPTZ 规范)。
- **FS2** `FlightSchdRepository` 接口与实装:快照批量写入、增量更新(ON CONFLICT 保留 FDAY)、域化差删、点查、`SCHD_GEN` SQL CAS、历史清场 `deleteByFlids`
- **FS3** `MessageProcessor` 事务 2 扩展:变更写入 + 事件写入 + `SUCCEEDED` 在单 PG 事务原子提交;Handler 视图按 FLID 点查。
- **FS4** `SnapshotFlow` SQL 化:单事务内批处理 upsert + 域内差删 + SQL CAS 推进 + 事件入队 + 终态标记;`JobExecutor` 清场删除闭环。
- **FS5** Redis 退役收口:删除 `FlightRedisClient`、Lua 脚本、`FlightRedisHealthIndicator``REDIS_FLIGHT_INFO` 投影与相关配置。
- **FS6** U29/U09 不变量测试门禁:崩溃幂等无自增、CAS 防并发、ADFT 存活保障、UTC 方言测试、ES 历史清场 5 场景全绿。
- **FS7** 影子对拍 Diff 工具:`FlightStoreDiffTool` 递归 AST 字段归一化比较与已知合法偏离识别。
- **FS8** 文档回改:架构、设计、用户故事与项目规范同步收敛。
## 8. 修订:11g 现场约束下废除 JSON 存储(本节追加,不改写上文定案历史)
> **触发**:现场环境只提供 Oracle 11g(无任何 JSON 能力——无 `JSON_VALUE`、无 `IS JSON` 约束、无 JSON 类型,
> JSON 支持自 12.1.0.2 才引入)。Q2 定案的「FLTR JSONB 全量」存储形态在该约束下不成立:
> 整文档存储丧失库端校验、字段级索引与直接 SQL 可查性,违背「运营航班可被 DBA/运营直接使用」的初衷。
**修订结论**
| # | 问题 | 修订结论 |
|---|---|---|
| R1 | 表形态 | 废除 `FLTR_JSON` JSONB 整文档,改为**一行一航班的宽表**:FLID 主键 + FDAY 代列 + SCHD.FLTR 标量字段列(锚定 `unisysaodbsis.xsd` 契约与 legacy 派生字段 ABDG/LPSDT/ABN)+ 1:N 明细集合序列化文本列。字段即列,天然可索引、可直查。 |
| R2 | 集合字段 | 登机口/柜台/转盘/桥/延误等 1:N 明细(单航班可达 99 条)暂存序列化文本列(`*_TXT`11g 移植为 CLOB);阶段 2/3 Handler 钉死语义后按需升独立子表。 |
| R3 | 值语义 | 全部字段保持 legacy 字符串原样(不做库端类型转换),保证影子对拍逐字段保真;范围查询需要的类型化列(如 SODT→DATE)按查询需求逐列后置提升。 |
| R4 | SCHD_GEN | `FLIDS_JSON` 同步废除,展开为 `SCHD_GEN_FLID(FDAY, FLID)` 行;版本 CAS 语义不变。 |
| R5 | 值机视图 | Handler 在线视图契约由 `flid → FLTR_JSON` 改为 `flid → 字段集`(与 legacy `hgetAllFlightInfo` hash 同构);增量写语义=字段级合并(与 legacy hmset 一致),快照=整体替换。 |
| R6 | 对拍口径 | FS7 Diff 工具改为 PG 宽表列值 vs legacy FLTR JSON 的逐字段归一化比对(数值精度/空值等价规则保留),比原 JSONB AST 比对更精确到字段。 |
**未尽事项(11g 方言移植不在本修订范围,另行决策)**:存储模型已 11g 兼容,但仓储层仍存 PG 方言
`ON CONFLICT` upsert 需改 `MERGE`)、I5 advisory lock 的 11g 替代(`DBMS_LOCK`)、Flyway/驱动
对 11.2 的支持矩阵。若「自有 PG → 现场 11g」成为确定部署形态,需按 ACM2-28 体例开独立决策记录
重开「自有库」平台定案,评估点在方言与并发原语,不在本修订已解决的存储形态。
## 9. ACM2-29 修正落盘:全宽表·零子表(Zero-Subtable Wide Table
R2 的「`*_TXT` 过渡 + 按需升独立子表」经 `docs/legacy/SIS_AODB_RMS-V0.1.md` 报文样例核验与
成都现场(双流/天府)地服实际规律复核,确认为对 XSD 理论上限(maxOccurs="99")的过度设计,
正式废除。16 个明细集合全部收敛为 `FLIGHT_SCHD` 宽表的标准 VARCHAR 标量列或紧凑格式化字串,
零子表、零 CLOB(V1.2.0 迁移落地):
- **平铺标量列**(槽位号即报文序号属性,可按需直接追加普通 B-tree):登机门 `GATE1/2+PGOT/PGCT/GOTM/GCTM/GTYP`
×2 组;值机柜台 `CHKC1..3` 全属性(同柜台分舱复用条目写侧抹平去重);转盘 `BELT1/2` 全属性
(计划时刻 `BPCOT/BPCCT` 专属前缀避免与值机同名);计划机位 `PSST1/2`;离港通道 `CHUT1/2`
(等级/类型 `CHCLS/CHTYP` 专属前缀)。槽位数按现场规律定界(2/3/2/2/2),超界条目按定案丢弃。
- **单值/里程碑标量列**:延误 `DELY_CODE/DELY_STRT/DELY_DURA/DELY_REMC`(业务上任意时刻仅 1 个
有效延误,覆盖语义);靠撤桥 `ABTM_A/ABTM_D`(桥号沿用派生列 ABDG);轮挡 `CHOT_ON/CHOT_OFF`
(机位沿用派生列 STND);异常 `FDIV/FRET/FLAB` 前缀标量列(规范 1:0..1 单值异常)。
- **紧凑 VARCHAR 字串列**(无界/航路型集合,11g VARCHAR2 内联存储,非 CLOB):
`ROUT_PATH/ERUT_PATH`(≤7 站紧凑航路 `"APCD/SCAT/SCDT,..."`);`SRVT_TEXT/VIPF_TEXT/MAFL_TEXT`
(服务/VIP/共享列表条数无 XSD 上限,存 JSON 数组;超长由列约束 fail fast,不静默截断)。
- **消息语义**:运营资源 = 单资源集合级全量快照替换(集合键出现即整集合覆盖,缺槽位置 NULL,
序号属性 "0" 为显式删除标记);外层航班字段仍为字段级增量合并(仅新增/覆盖)。
- **读侧视图**:由平铺列重建 16 个集合键,与 legacy `flightInfo` hash 同构——KAFKA_SCHD 线格式
FLTR JSON 数组)与 FS7 Diff 逐字段比对均依赖该同构视图,存储形态变化不出仓储边界。
已知保真边界:`ABTM/CHOT` 只保留里程碑时刻(资源号沿用 ABDG/STND 派生列);航路空串属性
与缺失属性在紧凑字串中合并为空槽位;`FDIV/FRET/FLAB` 自由文本键名待阶段 2 Handler 钉死。
- **出站序列化**`FlightFieldsJson` 对契约结构字段解析 JSON tree 输出原生数组/对象,
杜绝集合被再次编码成字符串的双重转义。
- **I5 单写者锁修正**:不再依赖 PG advisory lock/11g `DBMS_LOCK`。V1.2.0 创建 `PIPELINE_LOCK`
单行锁表,处理事务对 `FLIGHT_SCHD_WRITER` 行执行 `SELECT ... FOR UPDATE`;连接断开、提交或
回滚时由数据库释放,PG 与 11g 同构,无 DBA 特权依赖。
16 个 `*_TXT` 文本列由 V1.2.0 全部删除;运营查询索引(如 `GATE1/CHKC1/DELY_CODE`)按需以普通
B-tree 逐列追加,11g/PG 零方言成本。
Oracle 11g 的 `MERGE INTO` 方言、Flyway 11.2 支持版本和 ojdbc 认证组合仍必须在目标数据库环境
中完成平台决策与集成验收;不能用 PostgreSQL 测试结果替代该外部环境证据。
## 10. ACM2-29 v2 无损明细表方案落地(2026-09-08)
按 [评审报告](../flight-state.md)F1F9)与
[flight-state.md](../flight-state.md) 评审定案,撤销 §9「全宽表·零子表」
作为权威存储形态,落地无损模型;本节为当前有效规则索引:
- **存储**`FLIGHT_SCHD` 保留标量/异常前缀/无界文本列(V1.4.0 已 DROP 槽位、里程碑与紧凑
航路列及冗余索引);10 类重复集合由 9 张明细表 + `flight_route_point(ROUTE_KIND)` 承载,
PK `(FLID, ORDINAL)``SOURCE_SEQ` 不去重不设唯一(v2 §3.2)。
- **写路径**`persistNextStates` 是唯一写路径;引擎产生显式命令
Unchanged/Set/Clear/Replace/Apply),序号 0 条目仅在整集合清除时翻译为 Clear,
混排 fail fast;异常对象 `"null"`/`{}`/空串 = 显式清除置 NULL。
- **事务**PIPELINE_LOCK 行锁内复核快照身份(`schd_gen.last_message_id`)与当前态,
CAS 失败一律回滚 `FAILED(INFRA)`;提交后共享信箱回填失败落 `backfill_todo` 补偿重试,
成功终态不降级(v2 §5)。
- **读模型**:明细表为权威读;`flight_schd_display` 视图提供首/次资源与总数投影。
- **双库**`SqlDialect` 接缝(PG 实装;`Oracle11gDialect` 为编译级交付),
11g 激活门控见 `db/migration/oracle11g/README.md`——现场 11.2 实测通过前不切换。
- **现行设计**[flight-state.md](../flight-state.md)。
现行航班设计统一见 [运营航班状态设计](../flight-state.md)。
@@ -91,7 +91,7 @@ msgexchange-api 为成都机场 OMMS 消息交换服务(`com.gzzn.omms:msgexch
3. `updatefltrBridge`:将各航班 `ABTM` 中的 `ABDG` **按序直接追加**到派生字段 `abdg`(代码不插入分隔符——分隔符依赖 `ABDG` 数据本身是否带逗号),拼接结果若以逗号结尾则**仅删除末尾一个逗号**
4. 调用 `redisService.hmset("flightInfo", FLID→FLTR)` 写入航班集合。
说明:`RedisService.hmset` 实现为 `opsForHash().putAll`,仅新增/覆盖 field新快照中未出现的旧 field 不会被删除,即 `flightInfo` 为合并写入语义,**并非整体替换**。快照差集清理须由新系统显式实现(见 6 节迁移验收边界)
说明:`RedisService.hmset` 实现为 `opsForHash().putAll`,仅新增/覆盖 field这是 legacy 行为记录,不构成新系统的设计依据
刷新成功:调用 `sendDschd` 向 Kafka 主题 `msg` 发送日计划到达通知(`MSG` 仅保留 `META`,业务体为空),随后将该记录标记为已处理,返回成功。
@@ -233,7 +233,7 @@ msgexchange-api 为成都机场 OMMS 消息交换服务(`com.gzzn.omms:msgexch
3. `hmset("flightInfo", FLID→FLTR)` 合并写入(新增/覆盖 field,不删除快照中未出现的旧 field);
4. 成功后发送日计划到达通知至 Kafka `msg`,并标记已处理。
- 验收标准:
- Redis `flightInfo` 合并写入:报文内航班被新增/覆盖;快照中不存在而 Redis 已存在的旧航班不被清除(合并语义,非整体替换)
- Redis `flightInfo` 合并写入:报文内航班被新增/覆盖;该行为仅供 legacy 对拍使用
- 主航班 `MAFL` 包含各共享航班的 `FLID` / `FLNO`
- 各航班 `abdg``ABDG` 值按序直接追加的结果:分隔符依赖数据本身,代码不插入分隔符;仅当拼接结果以逗号结尾时删除末尾一个逗号。回放测试需按该真实机制设定期望(见 3.3);
- Kafka `msg` 收到仅含 `META` 的通知消息;
@@ -484,7 +484,7 @@ msgexchange-api 为成都机场 OMMS 消息交换服务(`com.gzzn.omms:msgexch
9. 明文口令直接存放于各环境 `application-*.yml`;默认激活环境为 dev`spring.profiles.active=dev`)。
10. 忽略清单在 test 环境配置为 `error-*`,与其他环境(`eror-*`)不一致。
11. `hstCondition` 各阈值按毫秒计算(配置注释亦为毫秒),而相关代码注释存在单位表述不一致。
12. `updateByDaySchd``RedisService.hmset``putAll`)合并写入:新日计划快照中未出现的旧航班不会被清除(见 3.3)。如采用整体替换语义,新系统须显式清理差集并以回放测试固定预期
12. `updateByDaySchd``RedisService.hmset``putAll`)合并写入;该实现细节仅描述 legacy 行为
13. `FDELHandler` 对共享航班的删除同样调用 `sendMsg` 发送 `msg` 删除消息(与"共享航班不直接发送"的通用规则不一致)。
14. `FDELHandler` 删除共享航班、`ADFTHandler` 添加共享航班时,对主航班 `MAFL` 的修改均仅作用于内存对象并进入发送缓冲,未调用 `saveFltr` 写回 Redis;FDEL 元素比对使用 `==``MAFLDATA.getFlid()``FLTR.getFLID()` 均为 `BigInteger`,故为引用比较而非值比较)。属遗留缺陷,新系统应采用值相等比较并在同一原子状态变更中持久化主/共享航班关系。
15. `/schd/sync` 时间解析使用 12 小时制 `hh`(无 AM/PM 标记),且对空值、区间、解析失败及服务返回值均无校验。
@@ -504,7 +504,7 @@ msgexchange-api 为成都机场 OMMS 消息交换服务(`com.gzzn.omms:msgexch
### 6.2 迁移验收边界
1. **快照替换语义**若日航班计划采用整体替换,须显式清理差集,并以回放测试固定「输入样本 → Redis 终态」预期(现役为合并语义,见 3.3)
1. **日计划语义**新系统的日计划处理规则以 [运营航班状态设计](../flight-state.md) 为准,不从 legacy Redis 行为推导
2. **共享航班增删链路**:决定保留或消除 FDEL 对共享航班的通知(US-B1 例外);主/共享航班关系(ADFT 添加、FDEL 删除)须在同一原子状态变更中持久化(修复 ADFT/FDEL 未写回与 FDEL `==` 引用比较缺陷,见 5.7-14)。
3. **可执行验收矩阵**:覆盖 3 个 SCHD 与 29 个 FLOP 子类型,维度为「输入样本 × Redis 状态变更 × Kafka 通知结果」;对当前无测试的关键路径(如 ACTT、CNCL、FDEL、RESP、DNLD、ADFT)补自动化用例。
4. **入站可靠性**:每类消息的幂等键、重复投递与多实例并发行为。