17 KiB
航班运行数据接入与当前状态管理设计
本模块从共享 MySQL 信箱接收 AODB 报文,在本地库维护航班当前态,再通过 Kafka 发布变化。
未标注内容即规范要求;开放与待定项汇总在 §10。
1. 目标与边界
- 当前态唯一:AODB 是业务事实来源,本地库是本模块唯一查询源。
- 严格有序:单活动主泵按信箱 FIFO 处理消息。
- 原子提交:航班状态、处理结果、待发事件在同一事务提交。
- 可恢复:信箱回填与 Kafka 投递可独立重试,不重算业务。
- 完整保存:变长资源用明细表,不用固定槽位截断。
边界:只同步报文、维护当前态,不推导取消、延误、备降等业务状态。PIPELINE_LOCK 只串行化数据库事务,不能代替消息认领、选主与故障切换。
2. 数据来源与语义
| 来源 | 作用 |
|---|---|
SCHD DNLD/RESP |
按 FLID upsert 完整航班基准状态,字段完整替换(§5);不做名单比对或差删 |
| SCHD ADFT | 新增或更新单个临时航班(§2.1) |
| FLOP | 更新单个航班运行变化,增量合并,不改运营日 |
| FDEL | 终止航班实例,标记删除(§6.2) |
| 历史任务 | 归档已结束航班并物理清除(§8) |
字段缺失两种含义:
- 完整快照:未出现 = AODB 已无该数据,清除本地旧值。
- 增量(FLOP):未出现 = 本次没改,保留本地值。
2.1 ADFT 语义待确认
ADFT 的字段缺失语义尚需按 SIS 条款及真实报文确认,确认前不得直接沿用 FLOP 的增量合并规则。若 ADFT 含足以确定运营日的 SODT,应据此设置 OPERATION_DAY,不得默认留 NULL。
3. 数据模型
3.1 航班实例身份
FLID:AODB 分配的实例 ID(Number(1-12),SIS §3.16.2),全部表的唯一关联键。FLNO:展示用航班号,可重复,不参与身份判定。OPERATION_DAY:所属运营日,一经确定不可变(§3.5、§5.3)。STATE:ACTIVE/DELETED。所有删除先标记;物理清除只发生在历史归档成功之后,归档结果记录在历史存储,不在当前态保留ARCHIVED状态。
同一航班号不同运营日 = 不同 FLID。
身份属性变更由 AODB 通过 FDEL 后续接 ADFT 表达。两条消息的 FLID 可能相同,也可能不同,本模块不得预设一定产生新 FLID:
FLID相同:按同一标识下的生命周期重激活处理(FDEL 置DELETED,ADFT 恢复ACTIVE);FLID不同:按旧实例终止、新实例建立处理。
本模块不得合并实例,也不得由航班号或日期推断两个 FLID 是同一实例。
3.2 表职责
| 表 | 粒度 | 职责 |
|---|---|---|
FLIGHT_SCHD |
每 FLID 一行 |
标量当前态、OPERATION_DAY、STATE、STATE_VERSION、最近消息 ID |
| 8 张资源明细表 | 每 FLID 多行 |
登机门、值机柜台、行李转盘、计划机位、滑槽、延误、靠撤桥、轮挡 |
FLIGHT_ROUTE_POINT |
每 FLID 多行 |
ROUT/ERUT 路线点,ROUTE_KIND 区分 |
PIPELINE_LOCK |
单行 | 串行化状态写事务 |
PROC_STATE |
每消息一行 | 处理状态与重试结果;兼作快照重放判定(§5.1) |
MSG_EVENT(outbox) |
每事件一行 | 状态、变更、删除通知 |
REQ_TRACK / COUTMSGS |
每请求一行 | 请求状态与出站 |
BACKFILL_TODO |
每待补偿一行 | 共享信箱回填任务 |
SCHD_SNAP_LOG |
每快照一行 | 只追加留痕,不参与决策(§5.5) |
明细表共 9 张、承载 10 类集合(ROUT 与 ERUT 共用路线表);ORDINAL 保留输入顺序,SOURCE_SEQ 只存上游序号。
分层:决策层(FLIGHT_SCHD 与各运行表)承担正确性;留痕层(SCHD_SNAP_LOG)只追加、可重建;证据层(报文归档)冷路径、仅用于重演审计,尚未交付。
3.3 完整当前态
主行 + 全部明细 = 完整当前态。写入前在内存生成完整新状态再落库,明细集合按组先删后插。展示视图是投影,不是权威。
3.4 代码共享与过站
- 主航班与共享航班是独立实例,各有
FLID;共享航班用CSOP/CSFT指向主航班,MAFL存共享列表。 TAOP/TAFL/TAID是过站关联,不是代码共享。- 删除时共享航班 FDEL 先于主航班;按
FLID各自处理,不做级联推断。
3.5 运营日
OPERATION_DAY 是运行保障日期,不是接收日或落库日。
计算规则:按机场运营日规则,由计划运行时间字段(SODT)与机场时区(Asia/Shanghai)计算;切日边界由业务配置,不得假设等于接收日期或自然日零点。单日请求范围到运营日的映射、多日窗口中每条记录的归日,均按此规则执行。
快照取覆盖日;FLOP 保留当前值;ADFT 或先于快照的 FLOP 暂为 NULL,被快照收录时补齐(ADFT 含 SODT 时直接计算,见 §2.1)。
延误跨日仍是同一 FLID、运营日不变——SIS §3.16 注释 1 明确日计划不含前日延误航班,保留与清理由本模块负责。
4. 更新模型
以 FLID 为唯一边界:
SCHD → 完整基准(字段替换,upsert;不改变 DELETED 状态)
FLOP/ADFT → 增量合并
FDEL → 标记删除(DELETED)
ADFT → 生命周期重激活(同 FLID)或新实例建立(不同 FLID)
历史任务 → 归档后物理清除(内部清理,不发业务删除事件)
- 每次成功写入推进
STATE_VERSION;状态、事件、处理终态同事务提交。 - 主链路无集合删除;删除入口只有 FDEL 与历史归档,均先标记后清除。
- 普通 SCHD 不承担恢复语义:
DELETED航班被快照收录时保持DELETED并记录冲突告警(§5.1);恢复入口只有 ADFT(§6.3)。
5. SCHD 处理
设消息 M,记录集 R。身份前提:同一 FLID 属于唯一运营日;FLID 不跨日迁移。
主链路入口为 applyScheduleRecords,对单日快照与滚动窗口统一适用;对单日快照不做任何名单层面的处理。
5.1 applyScheduleRecords(同一事务)
- 锁定
PIPELINE_LOCK。 M在PROC_STATE已有成功终态 → 重放,直接记幂等成功。- 校验报文完整性(§5.2),任一失败整包不落地。
- 对每条记录按 §3.5 确定其
OPERATION_DAY。 - 校验每个
FLID的既有OPERATION_DAY(§5.3)。 - 按完整快照语义 upsert 每个航班(§5.4);
DELETED航班保持DELETED,记录冲突告警SCHD_REVIVE_CONFLICT,不恢复ACTIVE。 - 逐航班推进
STATE_VERSION,登记状态事件与处理终态。
任一步失败整体回滚。
5.2 报文完整性
五项校验:RECS 0–9999 且等于实收 FLTR 数;每条记录含合法数字型 FLID;快照内不重复;每条记录的运营日可计算且在报文覆盖范围内。
记录完整与成员完整必须分开:
- 记录完整:某
FLID的字段是该航班的完整状态 → 允许覆盖字段。48 小时滚动窗口满足此维度。 - 成员完整:报文含某运营日全部
FLID。主链路不使用成员完整性;若将来引入名单对账,需另行设计并单独定案。
5.3 航班归属
| 主行情况 | 处理 |
|---|---|
| 不存在 | 创建,OPERATION_DAY 按记录计算,STATE = ACTIVE |
NULL |
首次确定 |
| 与记录计算值相同 | 按快照更新状态 |
| 与记录计算值不同 | 违反身份约束:不改写、整包拒绝 DEAD(PROTOCOL)、告警 SAME_FLID_ACROSS_OPERATION_DAYS |
第四行意味着串日、错发或数据污染:不静默保留,不迁移归属,整包拒绝后交人工确认。
5.4 字段语义
| 输入 | 字段缺失含义 |
|---|---|
DNLD/RESP |
AODB 已无该数据 → 清除本地旧值 |
FLOP |
本次没改 → 保留本地值 |
KAFKA_SCHD |
当前态没有该字段 → 消费者删除旧值 |
标量出现 Set、缺失 Clear;集合出现 Replace、缺失 Replace 空集。清除只覆盖正式映射的字段。
5.5 时序约束与留痕
快照与 FLOP 的覆盖顺序是核心时序问题。 SEQN 重启后不可比、不表达业务新旧,因此不启用陈旧拦截,只承诺按信箱接收顺序形成当前态。“快照比 FLOP 权威”不成立,不得对外承诺。较旧的 SCHD 晚于 FLOP 到达可能把新动态改回旧值——接收序模型的已知代价,靠上游顺序保证或下一次 FLOP 纠正。SEQN 仅用于同会话顺序观测(下降记 SEQN_REGRESSION 并告警)。
事务外追加一条 SCHD_SNAP_LOG:
| 字段 | 说明 |
|---|---|
MSG_ID / RECV_AT |
消息 ID、接收时刻(UTC) |
SCOPE_START / SCOPE_END |
报文覆盖的运营日范围(单日时两者相等) |
RECS / UPSERTED / DURATION_MS |
规模、写入数、耗时 |
RESULT |
COMMITTED / REPLAY_SKIPPED / ROLLED_BACK |
FLAGS |
EMPTY / RECS_DROP / SEQN_REGRESSION / DAY_MISMATCH / SCHD_REVIVE_CONFLICT |
ARCHIVE_KEY |
原文归档引用 |
规则:RESULT 与 FLAGS 分列(可“成功且告警”);一行 = 一次尝试,重放也记;留痕不参与决策,写失败只记指标;保留 90 天,按 (SCOPE_END, RECV_AT) 清理。
6. 动态事件与删除
6.1 FLOP
读取完整当前态 → 合并变化 → 保留运营日 → STATE_VERSION 加一 → 同事务登记 KAFKA_MSG、KAFKA_SCHD 与处理结果。
6.2 FDEL
同一事务内:
- 按
FLID定位。 STATE = ACTIVE:置DELETED,推进STATE_VERSION,明细保留,发布删除事件。STATE = DELETED:记幂等成功,不推进STATE_VERSION,不重复发布删除事件。- 不存在:记幂等成功(迟到、重复不报错)。
删除一律标记,不物理清除:FLID 复用未确认(§10)、删除与更新事件可能乱序(§7.3),物理清除统一由历史归档执行(§8.2)。共享航班 FDEL 先于主航班,各自处理,不做级联推断。
6.3 ADFT 恢复
ADFT 到达且 FLID 已存在、STATE = DELETED:这是同 FLID 生命周期重激活(§3.1),恢复 ACTIVE,推进 STATE_VERSION,登记状态事件。FLID 不存在则按新实例建立。
7. 对外处理
7.1 请求
经 REQ_TRACK 登记、COUTMSGS 发出;同类请求只留一条有效,新请求置旧为 EXPIRED。只有 RESP 完成 RQFD 请求,按(运营日、发送方、请求类型)匹配最新一条 PENDING;DNLD 是主动下发,独立处理,除现场确认的扩展规则外不得用于完成请求。协议无请求关联号,匹配规则待验收。
主链路不依赖逐日请求;当前只收定时下发的 48 小时窗口即为主链路的正常输入。
7.2 回填
提交后回填共享信箱;回填失败不得把已提交的 SUCCEEDED 改回 FAILED。
7.3 Kafka
KAFKA_SCHD 发整态:Dispatcher 合并同一 FLID 未发事件,按最新 STATE_VERSION 输出。KAFKA_MSG 只通知变化。两主题间不保证顺序;整态键缺失表示删除旧值。
判旧:消费端 (FLID, STATE_VERSION, UPDATED_AT)。
删除用 tombstone:载荷至少含 FLID、STATE_VERSION、DELETED;仅在 ACTIVE → DELETED 时发布,与删除同事务登记,投递失败持续重试。物理清除(§8.2)是内部清理,不再发布业务删除事件;仅对未经 FDEL、由生命周期直接清除的航班,在清除前补发一次删除事件。即使确认 FLID 永不复用,STATE_VERSION 也不能省——更新与删除事件仍会乱序;FLID 复用风险与 GENERATION 方案见 §10。
7.4 身份不变量
FLID是主键;一个FLID最多一个非空OPERATION_DAY,一经确定不可改。- 同
FLNO不同运营日 = 不同FLID。 - 身份变更时 FDEL 与 ADFT 的
FLID可能相同也可能不同(§3.1);不得由航班号或日期推断实例;FLID不同时是终止与新建,不是主键变更。
OPERATION_DAY 不可变由应用层在快照入口校验;需数据库层强化可加触发器或 WHERE operation_day IS NULL OR operation_day = :d。
8. 生命周期与清理
运营日过去不等于航班结束,不能按日期删除。
8.1 历史判定
单一时间窗不构成删除依据;须同时满足“超过配置时间窗”且有终态证据或足够长的静默期:
- 已取消(
CNCL非空)超过 48 小时; - 已完成离港/到港终态(
NAAT/NEAT,字段含义与来源系统需在术语表确认,§10)超过 48 小时; STATE = DELETED超过 48 小时;- 无终态字段时,最后一次有效更新超过兜底期限(默认 7 天)。
窗口按机场时区(Asia/Shanghai)计算。
8.2 归档与清除(顺序不可颠倒)
HISTORY_SWEEP选出满足 §8.1 的航班(含DELETED)。- 写入历史存储。
- 历史存储返回成功的
FLID集合,对应行物理删除主行与明细;归档结果只记录在历史存储,当前态不保留归档状态。 - 未经 FDEL 的航班在清除前补发一次删除事件(§7.3);其余不发。
- 失败或不明确的保留重试。
历史存储未接通时必须删 0 条;绝不允许先删当前态再补历史。物理清除只发生在本步骤。
8.3 保留期
| 对象 | 默认保留 | 前置条件 |
|---|---|---|
留痕 SCHD_SNAP_LOG |
90 天 | 按 (SCOPE_END, RECV_AT) 清理 |
OPERATION_DAY = NULL 航班 |
7 天 | 仍未被快照收录;终止规则未定前只增不删 |
9. 失败与读取
| 错误 | 处理 | 队头行为 |
|---|---|---|
| 非法报文、数量不符、容量超限 | DEAD,不重试 |
立即释放 |
| 未支持类型 | FAILED(UNSUPPORTED) |
达阈值转 DEAD |
| 数据库或内部故障 | FAILED(INFRA),退避重试 |
成功或超限前阻塞 |
快照含归属日不符的 FLID |
整包拒绝 DEAD(PROTOCOL),留痕告警 |
立即释放,交人工确认 |
SEQN 回退 / SCHD_REVIVE_CONFLICT |
只记 FLAGS 并告警 |
不阻塞 |
| 留痕写失败 | 只记 error 指标 | 不阻塞 |
读取:完整航班状态须在一致性读事务中读主表与全部明细,且过滤 STATE = ACTIVE。当前逐航班读取有 N+1 问题,主表与明细可能不在同一快照——批量加载与一致性读边界待补。
10. 未定事项
由 ACM2-31 跟踪:快照与动态事件顺序、FLOP 集合语义、删除事件版本、幂等键生命周期、FIFO 参数、一致性读取、快照完整性校验、Oracle 适配。
| 事项 | 状态 |
|---|---|
SCHD 按 FLID upsert 且不恢复 DELETED;删除一律标记,物理清除由历史归档执行 |
已定案(§4、§5.1、§6.2、§8.2) |
FDEL + ADFT 的 FLID 可能相同或不同 |
已定案为处理规则(§3.1、§6.3);上游实际行为待 SIS 条款核对 |
OPERATION_DAY 按机场运营日规则由 SODT 与机场时区计算,切日边界业务配置 |
已定案(§3.5);边界值待业务确认 |
| ADFT 字段缺失语义 | 待确认(§2.1);确认前不得沿用 FLOP 合并规则 |
FLID 是否会被复用 |
待上游书面确认;确认前保持 DELETED 中间态。若不能保证永不复用,需引入生命周期表 FLIGHT_ID_LIFECYCLE(FLID, GENERATION, LAST_VERSION),对外版本升级为 (FLID, GENERATION, STATE_VERSION) |
| 上游取消是否必然伴随 FDEL | 待上游确认;确认前不做任何基于名单缺席的删除 |
| 旧快照晚于 FLOP 的覆盖风险 | 接收序模型的已知代价;上游顺序保证待确认(ACM2-31#8) |
NAAT / NEAT 的业务含义与来源系统 |
待术语表确认(§8.1) |
| 逐运营日请求完整日计划 | 未完成;主链路不依赖,是否需要待上游 FDEL 可靠性确认后再评估 |
当前偏差:GTDT 按 Replace 处理但 FLOP 集合语义未确认;动态事件在锁外预览、事务内重算,目标只算一次;回填待办提交后写,崩溃窗口不可恢复,目标事务内预登记。
规则 M:快照与动态事件的应用顺序
当前实现按信箱接收顺序应用 SCHD 与 FLOP。SCHD 处理时完整替换对应字段,因此接收顺序上后到的 SCHD 会覆盖先到的 FLOP——这只是当前实现结果,不代表 SCHD 在业务时间上优先,不得对外承诺快照权威(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 |