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

11 KiB
Raw Blame History

航班运行数据接入与当前状态管理设计

本文说明航班当前态的设计逻辑:数据从哪里来、快照账本为什么存在、各表如何配合,以及消息如何完成事务提交和对外投递。

未标注内容是规范要求;“开放项”“未决”“已知偏差”尚未定案或尚未完成。

1. 目标与边界

系统从共享 MySQL 信箱接收 AODB 报文,在本地数据库维护航班当前态,再通过 Kafka 发布状态变化。

设计目标:

  1. 当前态唯一:AODB 是业务事实来源,本地数据库是本模块唯一查询源。
  2. 严格有序:单活动主泵按信箱 FIFO 处理消息。
  3. 原子提交:航班状态、快照账本、处理结果和 outbox 在同一事务提交。
  4. 可恢复:提交后的信箱回填和 Kafka 投递可独立重试,不重算业务。
  5. 完整保存:变长资源使用明细表,不用固定槽位截断。

本模块只同步报文和维护当前态,不推导取消、延误、备降、返航等业务状态。旅客服务状态不在范围内。

生产环境同一时刻只能有一个活动主泵。PIPELINE_LOCK 可以串行化数据库事务,但不能代替消息认领、实例选主和故障切换。

2. 数据来源与处理方式

来源 作用 处理方式
SCHD DNLD / RESP 建立某个 Operation Day 的完整基准 更新名单、写入航班、差删消失航班、推进快照版本
SCHD ADFT 新增或更新单个临时航班 不换代、不差删、不完成请求
FLOP 更新单个航班的运行变化 在当前态上合并,不修改 Operation Day

DNLD/RESP 是完整快照:未发送的可选字段或集合表示上游当前没有该数据,应清除本地旧值。FLOP 是增量事件:未发送字段保持不变。

开放项:ADFT 的缺失字段表示清除还是保持不变,尚待确认。FLOP 集合表示完整集合还是单次变化,也须逐类确认(ACM2-31#10)。

3. 核心数据模型

3.1 表及职责

粒度 职责
FLIGHT_SCHD 每个 FLID 一行 保存标量当前态、OPERATION_DAYSTATE_VERSION 和最近消息 ID
8 张资源明细表 每个 FLID 多行 保存登机门、值机柜台、行李转盘、计划机位、滑槽、延误、靠撤桥、轮挡
FLIGHT_ROUTE_POINT 每个 FLID 多行 保存 ROUT/ERUT 路线点,以 ROUTE_KIND 区分
SCHD_GEN 每个 Operation Day 一行 保存当前快照的 VERSIONLAST_MESSAGE_ID
SCHD_GEN_FLID 每个 Operation Day、每个 FLID 一行 保存当前快照的完整航班名单
PIPELINE_LOCK 单行 串行化状态写事务
PROC_STATE 每条消息一行 保存处理状态和重试结果
outbox 表 每个待投递事件一行 保存状态事件、变更通知和删除通知
REQ_TRACK / COUTMSGS 每个请求一行 保存请求状态和请求出站消息
BACKFILL_TODO 每个待补偿回填一行 保存共享信箱回填任务

9 张明细表承载 10 类集合,因为 ROUT 和 ERUT 共用路线表。ORDINAL 保留输入顺序;SOURCE_SEQ 只保存上游序号,不作为唯一键或去重键。

3.2 完整当前态

一个航班的完整当前态为:

FLIGHT_SCHD 主行
+ 该 FLID 在 9 张明细表中的全部记录
= 航班完整当前态

写入前先在内存中生成完整新状态,再更新主表和明细表。明细集合按组先删后插,保证数据库与内存状态一致。展示视图只是简化投影,不是权威状态,也不能用于完整数据比对。

3.3 Operation Day

OPERATION_DAYOperation Day(运营日),表示航班在运行保障业务上的日期,不是报文接收日或数据库落库日。

  • DNLD/RESP:取快照指定的 Operation Day。
  • FLOP:保留航班当前 Operation Day。
  • ADFT 或先于快照到达的 FLOP:无法取得时暂存 NULL,后续由快照补齐。

OPERATION_DAY=NULL 只表示运营日尚未取得,不表示航班没有运营日。

4. 快照账本设计

仅保存 FLIGHT_SCHD 无法判断上一份快照包含哪些航班,也无法安全差删、识别最近快照重放或防止并发推进。因此每个 Operation Day 使用两张账本表:

SCHD_GEN       保存当前快照版本和最近消息 ID
SCHD_GEN_FLID  保存当前快照的完整 FLID 名单

SCHD_GEN.VERSION 是运营日快照版本;FLIGHT_SCHD.STATE_VERSION 是单航班状态版本。FLOP 只推进后者,不修改快照账本。

同一运营日可以接收多份快照。每次成功提交都会替换名单并将快照版本加一;失败或最近快照重放不加版本。

5. 完整快照处理

设本次快照的 Operation Day 为 D,消息 ID 为 M,新名单为 N,账本旧名单为 O。以下步骤在同一事务完成:

  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,提交。

任一步失败,状态、名单、版本、处理结果和事件全部回滚。

5.1 差删为什么检查 Operation Day

旧名单只能说明“上一份 D 快照包含该航班”,不能说明航班现在仍属于 D。例如 C 先在 9 月 9 日名单中,随后被 9 月 10 日快照接管。新的 9 月 9 日快照不含 C 时,不能删除已经属于 9 月 10 日的 C。

最终删除集合为:

(旧名单 O  新名单 N)
∩ 当前 OPERATION_DAY 仍等于 D 的航班

这就是名单账本和主表 Operation Day 必须同时存在的原因。

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_MSGKAFKA_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,载荷只有 FLIDDELETED。它与删除操作同事务登记,投递失败持续重试。

开放项:删除事件没有版本号;迟到删除、删除后恢复和 STATE_VERSION 连续性尚未解决(ACM2-31#11)。

8. 生命周期与历史

本模块只保存当前态:不保存状态历史,不逐条投递中间版本,不提供历史查询,也不承诺保存原始报文。

OPERATION_DAY=NULL 的航班不参与快照差删。定期作业可清理超过 N 天且未被请求或快照名单引用的记录;N 默认 7 天,启用前须由消费方确认。

开放项:历史运营日账本和航班没有清理规则。清理不能破坏差删依赖的旧名单和 Operation Day。

9. 失败与读取

9.1 失败处理

错误 处理 队头行为
非法报文、数量不符、容量超限 DEAD,不重试 立即释放
未支持类型 FAILED(UNSUPPORTED) 达到阈值后转 DEAD
数据库或内部故障 FAILED(INFRA),退避重试 成功或超限前阻塞
快照 CAS 冲突 回滚并重读重试 成功前阻塞

最大阻塞时限、旁路规则和告警阈值仍是开放项(ACM2-31#13)。SEQN 的唯一范围、重置和回绕策略也未定义(ACM2-31#12)。

9.2 读取

完整航班状态必须在一致性读事务中读取主表和全部明细表。当前逐航班读取会产生 N+1 查询,且主表和明细表可能不在同一数据库快照。

待补:按 FLID 批量加载明细,并为查询和 Dispatcher 建立一致性读边界。GET /all/flights 尚未交付。

10. 当前范围与关键开放问题

当前只有 PostgreSQL 链路和 DNLD、GTDT 的部分流程接通。Oracle 11g、RESP、ADFT、请求出站、完整解析、回填崩溃恢复和一致性读取仍未完成。

关键开放问题由 ACM2-31 跟踪:FLID 生命周期、快照与动态事件顺序、Operation Day 回退、FLOP 集合语义、删除事件版本、幂等键生命周期、FIFO 参数、一致性读取、快照完整性校验和 Oracle 适配。

规则 M:同日快照与动态事件的优先级

当前候选规则是:同一 Operation Day 内,完整快照覆盖此前动态事件写入的重叠字段;快照未出现字段按完整快照语义清除。

该规则要求快照与动态事件的投递顺序等于上游发送顺序。协议尚未证明这一点,因此规则 M 不是已成立的不变量,也不能作为对外承诺(ACM2-31#8)。