Files
msgexchange-v2/docs/flight-state.md
T

210 lines
11 KiB
Markdown
Raw Normal View History

# 航班运行数据接入与当前状态管理设计
2026-09-09 11:43:34 +08:00
本文说明航班当前态的设计逻辑:数据从哪里来、快照账本为什么存在、各表如何配合,以及消息如何完成事务提交和对外投递。
2026-09-09 11:43:34 +08:00
未标注内容是规范要求;“开放项”“未决”“已知偏差”尚未定案或尚未完成。
2026-09-09 11:43:34 +08:00
## 1. 目标与边界
2026-09-09 11:43:34 +08:00
系统从共享 MySQL 信箱接收 AODB 报文,在本地数据库维护航班当前态,再通过 Kafka 发布状态变化。
2026-09-09 11:43:34 +08:00
设计目标:
2026-09-09 11:43:34 +08:00
1. **当前态唯一**:AODB 是业务事实来源,本地数据库是本模块唯一查询源。
2. **严格有序**:单活动主泵按信箱 FIFO 处理消息。
3. **原子提交**:航班状态、快照账本、处理结果和 outbox 在同一事务提交。
4. **可恢复**:提交后的信箱回填和 Kafka 投递可独立重试,不重算业务。
5. **完整保存**:变长资源使用明细表,不用固定槽位截断。
2026-09-09 11:43:34 +08:00
本模块只同步报文和维护当前态,不推导取消、延误、备降、返航等业务状态。旅客服务状态不在范围内。
2026-09-09 11:43:34 +08:00
生产环境同一时刻只能有一个活动主泵。`PIPELINE_LOCK` 可以串行化数据库事务,但不能代替消息认领、实例选主和故障切换。
2026-09-09 11:43:34 +08:00
## 2. 数据来源与处理方式
2026-09-09 11:43:34 +08:00
| 来源 | 作用 | 处理方式 |
|---|---|---|
| SCHD DNLD / RESP | 建立某个 Operation Day 的完整基准 | 更新名单、写入航班、差删消失航班、推进快照版本 |
| SCHD ADFT | 新增或更新单个临时航班 | 不换代、不差删、不完成请求 |
| FLOP | 更新单个航班的运行变化 | 在当前态上合并,不修改 Operation Day |
2026-09-09 11:43:34 +08:00
DNLD/RESP 是完整快照:未发送的可选字段或集合表示上游当前没有该数据,应清除本地旧值。FLOP 是增量事件:未发送字段保持不变。
2026-09-09 11:43:34 +08:00
**开放项**:ADFT 的缺失字段表示清除还是保持不变,尚待确认。FLOP 集合表示完整集合还是单次变化,也须逐类确认(ACM2-31#10)。
2026-09-09 11:43:34 +08:00
## 3. 核心数据模型
2026-09-09 11:43:34 +08:00
### 3.1 表及职责
2026-09-09 11:43:34 +08:00
| 表 | 粒度 | 职责 |
|---|---|---|
| `FLIGHT_SCHD` | 每个 `FLID` 一行 | 保存标量当前态、`OPERATION_DAY``STATE_VERSION` 和最近消息 ID |
| 8 张资源明细表 | 每个 `FLID` 多行 | 保存登机门、值机柜台、行李转盘、计划机位、滑槽、延误、靠撤桥、轮挡 |
| `FLIGHT_ROUTE_POINT` | 每个 `FLID` 多行 | 保存 ROUT/ERUT 路线点,以 `ROUTE_KIND` 区分 |
| `SCHD_GEN` | 每个 Operation Day 一行 | 保存当前快照的 `VERSION``LAST_MESSAGE_ID` |
| `SCHD_GEN_FLID` | 每个 Operation Day、每个 `FLID` 一行 | 保存当前快照的完整航班名单 |
| `PIPELINE_LOCK` | 单行 | 串行化状态写事务 |
| `PROC_STATE` | 每条消息一行 | 保存处理状态和重试结果 |
| outbox 表 | 每个待投递事件一行 | 保存状态事件、变更通知和删除通知 |
| `REQ_TRACK` / `COUTMSGS` | 每个请求一行 | 保存请求状态和请求出站消息 |
| `BACKFILL_TODO` | 每个待补偿回填一行 | 保存共享信箱回填任务 |
2026-09-09 11:43:34 +08:00
9 张明细表承载 10 类集合,因为 ROUT 和 ERUT 共用路线表。`ORDINAL` 保留输入顺序;`SOURCE_SEQ` 只保存上游序号,不作为唯一键或去重键。
2026-09-09 11:43:34 +08:00
### 3.2 完整当前态
2026-09-09 11:43:34 +08:00
一个航班的完整当前态为:
```text
2026-09-09 11:43:34 +08:00
FLIGHT_SCHD 主行
+ 该 FLID 在 9 张明细表中的全部记录
= 航班完整当前态
```
2026-09-09 11:43:34 +08:00
写入前先在内存中生成完整新状态,再更新主表和明细表。明细集合按组先删后插,保证数据库与内存状态一致。展示视图只是简化投影,不是权威状态,也不能用于完整数据比对。
2026-09-09 11:43:34 +08:00
### 3.3 Operation Day
2026-09-09 11:43:34 +08:00
`OPERATION_DAY`**Operation Day(运营日)**,表示航班在运行保障业务上的日期,不是报文接收日或数据库落库日。
2026-09-09 11:43:34 +08:00
- DNLD/RESP:取快照指定的 Operation Day。
- FLOP:保留航班当前 Operation Day。
- ADFT 或先于快照到达的 FLOP:无法取得时暂存 `NULL`,后续由快照补齐。
2026-09-09 11:43:34 +08:00
`OPERATION_DAY=NULL` 只表示运营日尚未取得,不表示航班没有运营日。
2026-09-09 11:43:34 +08:00
## 4. 快照账本设计
2026-09-09 11:43:34 +08:00
仅保存 `FLIGHT_SCHD` 无法判断上一份快照包含哪些航班,也无法安全差删、识别最近快照重放或防止并发推进。因此每个 Operation Day 使用两张账本表:
```text
2026-09-09 11:43:34 +08:00
SCHD_GEN 保存当前快照版本和最近消息 ID
SCHD_GEN_FLID 保存当前快照的完整 FLID 名单
```
2026-09-09 11:43:34 +08:00
`SCHD_GEN.VERSION` 是运营日快照版本;`FLIGHT_SCHD.STATE_VERSION` 是单航班状态版本。FLOP 只推进后者,不修改快照账本。
2026-09-09 11:43:34 +08:00
同一运营日可以接收多份快照。每次成功提交都会替换名单并将快照版本加一;失败或最近快照重放不加版本。
2026-09-09 11:43:34 +08:00
## 5. 完整快照处理
2026-09-09 11:43:34 +08:00
设本次快照的 Operation Day 为 D,消息 ID 为 M,新名单为 N,账本旧名单为 O。以下步骤在同一事务完成:
2026-09-09 11:43:34 +08:00
1. 锁定 `PIPELINE_LOCK`,读取 D 的账本和旧名单 O。
2. 若 M 等于 `SCHD_GEN.LAST_MESSAGE_ID`,按最近快照重放处理,不改状态、版本和事件。
3. 校验完整性:`RECS` 必须等于实收航班数,范围为 0–9999。
4. 为 N 中每个航班生成完整新状态,写入主表和明细表,设置 `OPERATION_DAY=D`
5. 计算候选删除集合 `O N`
6. 只删除候选集合中当前仍满足 `FLIGHT_SCHD.OPERATION_DAY=D` 的航班,并登记删除事件。
7. 用 N 替换 `SCHD_GEN_FLID` 的旧名单。
8. 通过 CAS 将 `SCHD_GEN.VERSION` 加一,并记录 M。
9. 登记状态事件和 `PROC_STATE=SUCCEEDED`,提交。
2026-09-09 11:43:34 +08:00
任一步失败,状态、名单、版本、处理结果和事件全部回滚。
2026-09-09 11:43:34 +08:00
### 5.1 差删为什么检查 Operation Day
2026-09-09 11:43:34 +08:00
旧名单只能说明“上一份 D 快照包含该航班”,不能说明航班现在仍属于 D。例如 C 先在 9 月 9 日名单中,随后被 9 月 10 日快照接管。新的 9 月 9 日快照不含 C 时,不能删除已经属于 9 月 10 日的 C。
2026-09-09 11:43:34 +08:00
最终删除集合为:
2026-09-09 11:43:34 +08:00
```text
(旧名单 O 新名单 N)
∩ 当前 OPERATION_DAY 仍等于 D 的航班
```
2026-09-09 11:43:34 +08:00
这就是名单账本和主表 Operation Day 必须同时存在的原因。
2026-09-09 11:43:34 +08:00
### 5.2 Operation Day 回退风险
差删检查只能防止误删,不能防止迟到的旧日快照把 `OPERATION_DAY` 从新日改回旧日,也不能防止旧字段覆盖新状态。
**开放项**:运营日迁移的时序规则尚未定案;当前不承诺 Operation Day 只向前迁移(ACM2-31#9)。
### 5.3 重放边界
- `identity_key` 负责入口消息去重。
- `SCHD_GEN.LAST_MESSAGE_ID` 只识别该运营日最近一份快照的重复执行。
- 事务失败后的同消息重试会重新执行完整事务。
- 更早的历史快照不会被 `LAST_MESSAGE_ID` 拦截。
人工重放历史快照必须带明确的版本和 Operation Day 策略,否则可能覆盖新状态。
## 6. 动态事件处理
FLOP 不建立快照,也不修改快照名单:
1. 根据 `FLID` 读取完整当前态。
2. 合并本次变化;未出现字段保持不变。
3. 保留当前 `OPERATION_DAY`;无法取得时写 `NULL`
4.`STATE_VERSION` 加一,写入主表和明细表。
5. 同事务登记 `KAFKA_MSG``KAFKA_SCHD` 和处理结果。
**已知偏差**:GTDT 当前按登机门集合 Replace 处理,但 FLOP 集合的完整/增量语义尚未确认。动态事件还在锁外生成事件预览、事务内再次计算状态;目标是只在事务内生成一次新状态,供落库和事件共用。
## 7. 请求与提交后处理
### 7.1 请求匹配
请求通过 `REQ_TRACK` 登记,通过 `COUTMSGS` 发送。同类请求只保留一条有效记录,新请求将旧请求置为 `EXPIRED`
RESP 按(Operation Day、发送方、请求类型)完成最新一条 `PENDING` 请求。迟到或重复应答仍可更新快照,但不重复完成请求。DNLD 仅在 Operation Day 匹配时完成请求。协议没有请求关联号,因此该匹配规则仍需验收。
### 7.2 信箱回填
业务事务提交后回填共享 MySQL 信箱。回填失败不得把已提交的 `SUCCEEDED` 改回 `FAILED`
**已知偏差**:当前只在回填失败后写 `BACKFILL_TODO`。若提交后、写待办前崩溃,任务无法恢复。目标是在业务事务中预先登记回填意图。
### 7.3 Kafka 投递
`KAFKA_SCHD` 是航班整态,不是补丁。Dispatcher 合并同一 `FLID` 的未发事件,读取数据库当前态并按最新 `STATE_VERSION` 输出;中间版本不逐条发送。
- 消费端按 `(FLID, STATE_VERSION, UPDATED_AT)` 拒绝旧状态覆盖新状态。
- `KAFKA_MSG` 只通知变化,不承载完整状态。
- 两个主题不保证跨主题顺序。
- 整态中键缺失表示该数据不存在,消费端必须删除旧值。
差删使用自定义 tombstonevalue 非 null,载荷只有 `FLID``DELETED`。它与删除操作同事务登记,投递失败持续重试。
**开放项**:删除事件没有版本号;迟到删除、删除后恢复和 `STATE_VERSION` 连续性尚未解决(ACM2-31#11)。
## 8. 生命周期与历史
本模块只保存当前态:不保存状态历史,不逐条投递中间版本,不提供历史查询,也不承诺保存原始报文。
`OPERATION_DAY=NULL` 的航班不参与快照差删。定期作业可清理超过 N 天且未被请求或快照名单引用的记录;N 默认 7 天,启用前须由消费方确认。
**开放项**:历史运营日账本和航班没有清理规则。清理不能破坏差删依赖的旧名单和 Operation Day。
## 9. 失败与读取
### 9.1 失败处理
| 错误 | 处理 | 队头行为 |
|---|---|---|
2026-09-09 11:43:34 +08:00
| 非法报文、数量不符、容量超限 | `DEAD`,不重试 | 立即释放 |
| 未支持类型 | `FAILED(UNSUPPORTED)` | 达到阈值后转 `DEAD` |
| 数据库或内部故障 | `FAILED(INFRA)`,退避重试 | 成功或超限前阻塞 |
| 快照 CAS 冲突 | 回滚并重读重试 | 成功前阻塞 |
2026-09-09 11:43:34 +08:00
最大阻塞时限、旁路规则和告警阈值仍是开放项(ACM2-31#13)。SEQN 的唯一范围、重置和回绕策略也未定义(ACM2-31#12)。
2026-09-09 11:43:34 +08:00
### 9.2 读取
2026-09-09 11:43:34 +08:00
完整航班状态必须在一致性读事务中读取主表和全部明细表。当前逐航班读取会产生 N+1 查询,且主表和明细表可能不在同一数据库快照。
2026-09-09 11:43:34 +08:00
**待补**:按 FLID 批量加载明细,并为查询和 Dispatcher 建立一致性读边界。`GET /all/flights` 尚未交付。
2026-09-09 11:43:34 +08:00
## 10. 当前范围与关键开放问题
2026-09-09 11:43:34 +08:00
当前只有 PostgreSQL 链路和 DNLD、GTDT 的部分流程接通。Oracle 11g、RESP、ADFT、请求出站、完整解析、回填崩溃恢复和一致性读取仍未完成。
2026-09-09 11:43:34 +08:00
关键开放问题由 ACM2-31 跟踪:FLID 生命周期、快照与动态事件顺序、Operation Day 回退、FLOP 集合语义、删除事件版本、幂等键生命周期、FIFO 参数、一致性读取、快照完整性校验和 Oracle 适配。
2026-09-09 11:43:34 +08:00
### 规则 M:同日快照与动态事件的优先级
2026-09-09 11:43:34 +08:00
当前候选规则是:同一 Operation Day 内,完整快照覆盖此前动态事件写入的重叠字段;快照未出现字段按完整快照语义清除。
2026-09-09 11:43:34 +08:00
该规则要求快照与动态事件的投递顺序等于上游发送顺序。协议尚未证明这一点,因此规则 M 不是已成立的不变量,也不能作为对外承诺(ACM2-31#8)。