Files
msgexchange-v2/docs/flight-state.md
T
windyboy 9e434d6486 docs(flight-state): 重写当前态设计为简洁版并落定航班身份模型 (ACM2-32)
按评审意见重写 docs/flight-state.md(603 → 332 行),去掉堆叠的补充说明,每条规则只保留一处:

身份模型:
- FLID 由 AODB 分配、与运营日绑定;同一 FLNO 不同运营日 = 不同 FLID
- OPERATION_DAY 一经确定不可变,跨日归属不符属协议异常(整包拒绝)
- 延误跨日不是迁移:同一 FLID、运营日不变,由 FLOP 继续更新
- 补齐 FDEL 删除入口(§2、§6.2):按 FLID 删除并移出名单,不换代
- 代码共享与过站关联分离;物理列名 FDAY 统一改称 OPERATION_DAY

快照与差删:
- 11 步事务固定顺序;差删集合即 O − N,OPERATION_DAY = D 为删除前断言
- 覆盖范围按“是否单个完整运营日”判定:48 小时窗口只覆盖状态、不差删
- 完整快照字段缺失即清除(SIS §3.16 注释 2/4),只对本模块映射字段生效
- SEQN 仅用于同会话顺序观测,不启用陈旧拦截

其余:留痕 RESULT/FLAGS 分列、身份不变量六条、保留期默认 7/90/7 天、
未定事项与 SIS 规范依据各收进一张表(§10)。
2026-09-09 14:54:54 +08:00

19 KiB
Raw Blame History

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

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

未标注内容即规范要求。标注“开放项”“已知偏差”“待定”的内容尚未定案或未完成,全部汇总在 §10。

1. 目标与边界

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

边界:只同步报文、维护当前态,不推导取消、延误、备降、返航等业务状态;旅客服务状态不在范围内。PIPELINE_LOCK 只能串行化数据库事务,不能代替消息认领、实例选主和故障切换。

2. 数据来源与语义

来源 作用 处理方式
SCHD DNLD / RESP 建立某运营日的基准名单 覆盖单个完整运营日时才换代与差删(§5.2);定时下发的 48 小时窗口只做状态覆盖与 upsert
SCHD ADFT 新增或更新单个临时航班 增量合并;不换代、不差删、不完成请求
FLOP 更新单个航班的运行变化 增量合并;不修改运营日、不改名册
FDEL 终止某个航班运行实例 FLID 删除并移出名单(§6.2);不换代

字段缺失只有两种含义:

  • 完整快照(DNLD/RESP):未出现表示 AODB 当前没有该数据,清除本地旧值
  • 增量(FLOP/ADFT):未出现表示本次没改,保留本地值

3. 数据模型

3.1 航班运行实例身份

  • FLIDAODB 分配的航班运行实例 IDNumber(1-12),SIS §3.16.2),是主表、明细表、动态事件、删除事件的唯一关联键。
  • FLNO:展示用航班号,可重复,不参与身份判定。
  • OPERATION_DAY:该实例所属运营日,一经确定不可变(§5.3)。

同一航班号在不同运营日执行 = 不同 FLID

输入 结果
9/9 快照含 FLID=121112312, FLNO=CA100 建实例 121112312OPERATION_DAY=9/9
9/10 快照含 FLID=121119845, FLNO=CA100 建实例 121119845OPERATION_DAY=9/10
9/9 新快照不含 121112312 进入 9/9 的 O N 差删
9/10 快照仍含 121119845 不受 9/9 差删影响
9/10 快照错误含 121112312 协议异常;不得把旧实例改归 9/10

身份变更由 AODB 用「旧 FLID 的 FDEL + 新 FLID 的 ADFT」表达,本模块不得把两者合并为同一实例,也不得通过航班号或日期推断两个 FLID 是同一实例。

3.2 表职责

粒度 职责
FLIGHT_SCHD 每个 FLID 一行 标量当前态、OPERATION_DAYSTATE_VERSION、最近消息 ID
8 张资源明细表 每个 FLID 多行 登机门、值机柜台、行李转盘、计划机位、滑槽、延误、靠撤桥、轮挡
FLIGHT_ROUTE_POINT 每个 FLID 多行 ROUT/ERUT 路线点,以 ROUTE_KIND 区分
SCHD_GEN 每个运营日一行 VERSIONLAST_MESSAGE_IDLAST_RECV_AT(上次提交时刻)
SCHD_GEN_FLID 每个运营日、每个 FLID 一行 该日最新一代快照的完整名单,差删依据
PIPELINE_LOCK 单行 串行化状态写事务
PROC_STATE 每条消息一行 处理状态与重试结果
MSG_EVENToutbox 每个待投递事件一行 状态事件、变更通知、删除通知
REQ_TRACK / COUTMSGS 每个请求一行 请求状态与请求出站
BACKFILL_TODO 每个待补偿回填一行 共享信箱回填任务
SCHD_SNAP_LOG 每份快照一行 只追加快照留痕,不参与决策(§5.6

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

数据分三层,职责不得串用:决策层(上表除 SCHD_SNAP_LOG 外的表)承担正确性;留痕层SCHD_SNAP_LOG)只追加、可丢弃重建;证据层(原始报文归档)冷路径、仅用于重演与审计,尚未交付

3.3 完整当前态

主行 + 该 FLID 在 9 张明细表中的全部记录 = 完整当前态。写入前先在内存生成完整新状态再落库,明细集合按组先删后插。展示视图是投影,不是权威,也不能用于完整比对。

3.4 代码共享与过站

  • 主航班与代码共享航班是各自独立的实例,各有 FLIDAODB 分别发记录与 FLOP。
  • 共享航班用 CSOPCSFT 指向主航班承运人代码与航班号;MAFL 存共享航班列表。
  • TAOP / TAFL / TAID 是过站关联(进港与后续出港),不是代码共享。
  • 删除时共享航班的 FDEL 先于主航班;本模块按 FLID 各自处理,不做级联推断。

3.5 运营日

OPERATION_DAY 是运行保障意义上的日期,不是报文接收日或落库日。

  • DNLD/RESP:取快照覆盖运营日。
  • FLOP:保留当前值。
  • ADFT 或先于快照到达的 FLOP:暂为 NULL,被快照收录时补齐。

NULL 只表示尚未取得。延误跨日的航班仍是同一 FLID、运营日不变,由 FLOP 继续更新——SIS §3.16 注释 1 明确日计划不含前日延误航班,保留与清理由本模块负责。

4. 快照账本

主表一行只记当前状态与归属,答不了三件事:上一份快照包含哪些航班、最近一份快照是不是重放、并发推进有没有冲突。所以每个运营日另用两张表:

SCHD_GEN       保存当前快照版本、最近消息 ID、上次提交时刻
SCHD_GEN_FLID  保存当前快照的完整 FLID 名单(管辖范围,不是归属)
  • SCHD_GEN.VERSION 是运营日快照版本;FLIGHT_SCHD.STATE_VERSION 是单航班状态版本,FLOP 只推进后者。
  • 名单记管辖范围(这份快照管了谁),不是归属:一个 FLID 只出现在一个运营日的名单下,各日名单互不相交。
  • 同一运营日可收多份快照:每次成功提交替换名单并版本加一;失败或最近快照重放不加版本。

5. 快照处理

设快照覆盖运营日 D,消息 ID M,记录集合 R,新成员集 N = {record.FLID | record ∈ R},上一版成员集 O。两条前提:

同一 FLID                → 一个航班运行实例 → 一个运营日
同一 FLNO + 不同运营日     → 不同 FLID

因此:FLID 不跨日迁移;OPERATION_DAY 一经确定不可改;不同运营日的快照互不接管对方航班。

5.1 处理步骤(同一事务)

  1. 锁定 PIPELINE_LOCKSCHD_GEN(OPERATION_DAY=D)
  2. M = LAST_MESSAGE_ID → 最近快照重放:不改状态、名单、版本、事件,直接记成功。
  3. 校验完整性与覆盖范围(§5.2);任一失败整包不落地。
  4. 校验 N 中每个 FLID 的既有 OPERATION_DAY(§5.3)。
  5. 计算差删集合 O N
  6. 按完整快照语义写入 N 中每个航班(§5.4)。
  7. 删除 O N,删除前断言 OPERATION_DAY = D
  8. 用 N 替换 SCHD_GEN_FLID(D) 名单。
  9. CASSCHD_GEN.VERSION = V + 1,记录 M
  10. 登记状态事件、删除事件与处理终态。
  11. 提交。

任一步失败:航班状态、成员名单、快照版本、删除结果、待发事件、处理终态全部回滚。

5.2 校验与覆盖范围

六项校验:RECS 在 09999RECS 等于实收 FLTR 数;每条记录含合法数字型 FLID;快照内 FLID 不重复;每条记录运营日 = D;覆盖单个完整运营日。

覆盖范围按“是否为单个完整运营日”判定,与报文子类型无关(SIS §3.16 Data Range):

来源 覆盖范围 换代与差删
按单运营日请求得到的日计划 该日完整成员集
AODB 定时下发的日计划 未来约 48 小时,跨多个运营日 否,只覆盖状态

判定落在接入侧:请求时指定单日,或按 SODT 分组并确认该组覆盖完整运营日。字段完整性与名单覆盖是两件事——窗口快照里每条记录仍是该 FLID 的完整字段状态,只是名单不完整。

5.3 航班归属

主行情况 处理
不存在 创建,OPERATION_DAY = D
NULL 首次确定为 D
= D 不变,按快照更新状态
≠ D 违反身份约束:不改写、整包拒绝、告警 SAME_FLID_ACROSS_OPERATION_DAYS

第四行在合法流程不会出现,它意味着串日、错发或数据污染。既不静默保留(会掩盖错误),也不迁移归属(业务上没有这个动作):整包拒绝后交人工确认。

5.4 字段语义

输入 字段缺失含义
DNLD/RESP 快照 AODB 没有该数据 → 清除本地旧值
FLOP 动态事件 本次没改 → 保留本地值
KAFKA_SCHD 整态 当前态没有该字段 → 消费者删除旧值

写入规则:标量出现 Set、缺失 Clear;集合出现 Replace、缺失 Replace 空集。清除只对本模块正式映射的字段执行,声明不消费的字段忽略。

5.5 差删

差删集合就是 O NOPERATION_DAY = D 是删除前必须成立的校验,不是过滤后静默跳过的条件:

for flid in (O  N):
    if not exists(flid):          continue   # 已删除或幂等重放
    if flight.OPERATION_DAY != D: fail       # 账本或主表不一致,整包失败并告警
    删除明细与主行;登记删除事件

出现 OPERATION_DAY ≠ D 是数据一致性错误,不是合法跨日场景。名单同时是差删的安全边界:不在 O 内的航班不删——少删可由下一次快照补上,多删不可自愈;账本不存在时不执行差删。RECS=0 的空快照仍会提交,旧名单中属于 D 的航班全部进入差删。

5.6 时序与留痕

快照完整不等于无条件覆盖后来的动态事件。“快照是否比当前状态旧”需要一个跨 SCHD 与 FLOP 可比较的上游顺序,而 SEQN 不是:它由消息服务器分配,服务器不重启时不重复且单调递增,重启后不可比,也不表达业务新旧。

因此当前不启用陈旧拦截,按信箱接收顺序处理;SEQN 只用于同一会话内的顺序观测(序号下降即记 SEQN_REGRESSION 并告警)。“快照最高权威”不是已成立的不变量,不得对外承诺。

主链路失败即整体回滚,外部看不见系统做过什么,所以在事务之外只追加一条 SCHD_SNAP_LOG

字段 说明
OPERATION_DAY / MSG_ID / RECV_AT 运营日、消息 ID、接收时刻(UTC
RECS / OLD_VERSION / NEW_VERSION / DELETED_COUNT 规模与结果
RESULT COMMITTED / REPLAY_SKIPPED / ROLLED_BACK
FLAGS EMPTY / RECS_DROP / OUT_OF_ORDER / SEQN_REGRESSION / DAY_MISMATCH
OLD_FLID_DIGEST / NEW_FLID_DIGEST 新旧名单指纹,审计名单抖动
ARCHIVE_KEY / DURATION_MS 原文归档引用与耗时

规则:

  • RESULTFLAGS 分列:一次提交可以同时“成功”且“触发告警”。
  • 一行 = 一次处理尝试;不建 (OPERATION_DAY, MSG_ID) 唯一键,重放也记一行。
  • 留痕不参与任何决策:写失败只记 error 指标,不改变已提交事务的结果;留痕成功也不代表事务成功。
  • 观测告警与状态变更同一事务登记,避免“异常已提交但告警丢失”或反向错配。DAY_MISMATCH 即 §5.3 的归属不符。
  • 保留 90 天,按 (OPERATION_DAY, RECV_AT) 清理。

6. 动态事件与删除

6.1 FLOP

FLOP 不建立快照也不改名册:读取完整当前态 → 合并本次变化 → 保留运营日(无则 NULL)→ STATE_VERSION 加一 → 同事务登记 KAFKA_MSGKAFKA_SCHD 与处理结果。

6.2 FDEL

FDEL 表示 AODB 终止某个航班运行实例(SIS §3.29)。它与快照差删是两个入口,对外事件形态一致。同一事务内:

  1. FLID 定位;不存在视为幂等成功(迟到、重复不报错)。
  2. 删除主行与 9 张明细表。
  3. 从该 FLID 所属运营日的 SCHD_GEN_FLID 名单中移除它——否则该日下一次快照还会把它算进 O N
  4. 登记删除事件与处理终态。

FDEL 不推进 SCHD_GEN.VERSION、不改 LAST_MESSAGE_ID、不触发差删。共享航班的 FDEL 先于主航班到达,按 FLID 各自处理,不做级联推断。

7. 对外处理

7.1 请求

请求经 REQ_TRACK 登记、COUTMSGS 发出;同类请求只保留一条有效,新请求把旧的置为 EXPIREDRESP 按(运营日、发送方、请求类型)完成最新一条 PENDINGDNLD 仅在运营日匹配时完成请求。协议没有请求关联号,匹配规则仍需验收。

差删只认覆盖单个完整运营日的快照,所以本模块必须为每个运营日至少主动请求一次完整日计划。当前请求出站尚未完成,实际只收到定时下发的 48 小时窗口,差删因此没有合法输入、名单只增不删——这是现状,不是目标。

7.2 回填

事务提交后回填共享信箱;回填失败不得把已提交的 SUCCEEDED 改回 FAILED

7.3 Kafka

KAFKA_SCHD 发航班整态,不是补丁:Dispatcher 合并同一 FLID 的未发事件,按最新 STATE_VERSION 输出,中间版本不逐条发送。KAFKA_MSG 只通知变化。两个主题之间不保证顺序。整态里键缺失表示该数据不存在,消费者应删除旧值。

判旧方式:消费端 (FLID, STATE_VERSION, UPDATED_AT);日代侧 (OPERATION_DAY, VERSION)VERSION 只在单运营日内单调,不得裸比较。

删除用 tombstonevalue 非 null,载荷至少含 FLIDSTATE_VERSIONDELETED;与删除操作同事务登记,投递失败持续重试。即使将来确认 FLID 永不复用,STATE_VERSION 也不能省——同一 FLID 的更新与删除事件仍会乱序。

7.4 身份不变量

  • FLIGHT_SCHD.FLID 是主键,OPERATION_DAY 可空。
  • 一个 FLID 最多一个非空 OPERATION_DAY,且一经确定不得修改。
  • FLNO 不同运营日 = 不同 FLID,各自独立存储。
  • 状态合并、STATE_VERSION、明细集合、Kafka 事件都以 FLID 为边界。
  • 不允许通过航班号或日期推断两个 FLID 是同一实例。
  • FDEL + ADFT 是旧实例终止与新实例建立,不是主键变更。

OPERATION_DAY 不可变由应用层在快照入口校验;要数据库层强化,可加触发器或把更新写成 WHERE operation_day IS NULL OR operation_day = :d

8. 生命周期与清理

运营日过去不等于航班结束:可能延误、跨日运行、返航或仍在收动态,不能在午夜按日期删除。

8.1 历史判定

满足任一条件即判为历史(默认值可配,未接线前不生效):

  • 计划开始时间 SODT 超过 48 小时;
  • 取消(CNCL 非空)超过 48 小时;
  • 备降航班已结束本场处理(判定字段待确认,未确认前该条不生效);
  • 离港关门 NAAT 超过 48 小时;
  • 到港下机完成 NEAT 超过 48 小时。

窗口按 SODT 所属运营日、机场本地时区(Asia/Shanghai)计算,不按落库日或 UTC 日期。

8.2 归档与清除(顺序不可颠倒)

  1. HISTORY_SWEEP 选出已结束航班。
  2. 写入历史存储。
  3. 历史存储返回成功的 FLID 集合。
  4. 只删除该集合对应的主行与明细。
  5. 登记对外删除事件。
  6. 失败或不明确的继续保留,下次重试。

历史存储未接通时成功集合为空,必须删 0 条;绝不允许先删当前态再补历史。

8.3 保留期与清理

对象 默认保留 清理前置条件
账本 SCHD_GEN / SCHD_GEN_FLID cutoff = 今天 7 天 该日无未终态消息与未清回填;该日待归档航班已写入历史;不破坏仍在使用的差删依据
留痕 SCHD_SNAP_LOG 90 天 (OPERATION_DAY, RECV_AT) 清理
OPERATION_DAY = NULL 航班 7 天 仍未被任何快照收录;终止规则未定前只增不删

删除 SCHD_GEN 时外键级联删除该日名单。未配置 cutoff 时不清理。

9. 失败与读取

错误 处理 队头行为
非法报文、数量不符、容量超限 DEAD,不重试 立即释放
未支持类型 FAILED(UNSUPPORTED) 达阈值转 DEAD
数据库或内部故障 FAILED(INFRA),退避重试 成功或超限前阻塞
快照 CAS 冲突 回滚重读重试 成功前阻塞
快照含归属日不符的 FLID 整包拒绝 DEAD(PROTOCOL),留痕告警 立即释放,交人工确认
SEQN 回退(同会话内序号下降) 只记 FLAGS 并告警 不阻塞
留痕写失败 只记 error 指标,不改变已提交结果 不阻塞

读取:完整航班状态必须在一致性读事务中读主表与全部明细表。当前逐航班读取有 N+1 问题,且主表与明细可能不在同一数据库快照——批量加载与一致性读边界待补。

10. 未定事项

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

事项 状态
FLID 与运营日绑定、不跨日迁移 已定案(§3.1、§5.3);原 ACM2-31#9 改为入口身份一致性校验
日计划覆盖范围按“是否单日完整”判定 已定案(§5.2
SEQN 可用边界 已明确:服务器不重启时单调,重启后不可比(§5.6);唯一范围与回绕未定义(ACM2-31#12)
FLID 是否会被复用 待上游书面确认;确认前删除与恢复设计不能只依赖 FLID
备降“已结束本场处理”的判定字段 待确认(§8.1
逐运营日请求完整日计划 未完成;在此之前差删无合法输入(§7.1)

当前偏差:GTDT 按登机门集合 Replace 处理,但 FLOP 集合语义未确认;动态事件在锁外生成事件预览、事务内再算一次,目标是只算一次;回填待办在提交后写,崩溃窗口内不可恢复,目标是事务内预登记。

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

同一运营日内,完整快照覆盖此前动态事件写入的重叠字段;快照未出现的字段按快照语义清除。该规则要求投递顺序等于上游发送顺序,协议尚未证明,因此它不是已成立的不变量,不得对外承诺(ACM2-31#8)。

规范依据(SIS

结论 出处
以 AODB 最新数据覆盖本地 §1.6.2
P 标签为空 = 删除该值;O 标签仅在有值时出现 §1.6.3
日计划是完整快照;未发送的可选字段表示 AODB 已无该数据,应删除 §3.16 注释 2、4
子系统不消费的字段可忽略 §3.16 注释 3
日计划不含前日延误航班,保留与清理由子系统负责 §3.16 注释 1
主动下发为未来约 48 小时窗口,请求应答按请求范围 §3.16 Data Range
代码共享为独立航班实例,删除时共享先于主航班 §1.6.5