docs: 航班状态设计重写为当前基线,按请求/下载与动态两条线讲清状态变更 (ACM2-30)
This commit is contained in:
+132
-163
@@ -1,215 +1,184 @@
|
||||
# 运营航班状态数据库设计 v2
|
||||
# 运营航班状态设计
|
||||
|
||||
状态:提案,待 ACM2-29 评审确认
|
||||
日期:2026-09-08
|
||||
适用部署:现场提供数据库时使用 Oracle 11g;现场不提供时使用自建 PostgreSQL。
|
||||
状态:PostgreSQL 实现基线,2026-09-08 核对。未完成项在各节明确标注;文件名沿用 v2,兼容历史引用。
|
||||
|
||||
## 1. 目标和边界
|
||||
配套文档:完成度证据见 [ACM2-29 核查与简化计划](acm2-29-audit-and-simplification.md),字段级行为见 [语义矩阵](flight-state-semantics.md),决策理由见 [决策摘要](decision-flight-state.md)。
|
||||
|
||||
本设计为 msgexchange-v2 提供一个可恢复、可查询、可审计的运营航班当前状态库。系统接收共享 MySQL 信箱中的 SIS 报文,解析后在自有数据库中形成当前航班状态,并通过 outbox 投递 Kafka 和出站信箱。
|
||||
## 1. 系统做什么
|
||||
|
||||
本系统不是 AODB 主数据编辑系统,不负责票务、旅客、机队维护或资源排班优化。上游 AODB/CIIMS 仍是业务报文来源;本系统的“权威”仅表示本系统已成功处理的当前状态和待发送事件。
|
||||
系统从共享 MySQL 信箱接收 SIS 报文,把报文合并成本系统的"航班当前状态",再把变化以事件发出。
|
||||
|
||||
设计目标:
|
||||
三条底线:
|
||||
|
||||
- 航班主表、资源明细、快照版本、处理终态和 outbox 在一个本地事务中提交。
|
||||
- 保留 SIS 重复元素的顺序、源序号、资源号和全部属性,不因页面列数或现场样例缩减数据。
|
||||
- 明确区分“字段未出现”“设置值”“显式清除”“集合替换”。
|
||||
- 同一业务模型支持 PostgreSQL 和 Oracle 11g,数据库方言只存在于基础设施适配层。
|
||||
- 在迁移、重放、回填失败和进程崩溃后,可以判断哪些业务已经提交,避免重复应用。
|
||||
- **一套权威状态。** 读写都指向本地数据库;共享 MySQL 和 Kafka 是提交之后的外部边界,失败各自重试,不会让已提交的状态回滚或重做。
|
||||
- **一个活动主泵。** 生产同时只有一个实例在处理;事务行锁只保护本地提交,不是多实例协调机制。
|
||||
- **一条提交事务。** 状态、outbox、处理终态在同一个本地事务里落库,要么全成功要么全回滚。
|
||||
|
||||
## 2. 核心不变量
|
||||
处理顺序严格按信箱 FIFO,按业务身份去重,失败按退避重试。
|
||||
|
||||
1. `FLIGHT_SCHD` 是当前航班状态的唯一写入目标;Redis 不参与动态状态权威。
|
||||
2. 只有主泵写入航班状态和快照成员。跨实例保护使用 `PIPELINE_LOCK` 行锁,但生产仍只允许一个活动实例。
|
||||
3. 一条消息的航班变更、明细变更、快照版本、outbox 事件和 `PROC_STATE=SUCCEEDED` 同事务提交;任一写入失败全部回滚。
|
||||
4. 快照差删只删除上一代在同一 `FDAY` 域内的成员;`ADFT` 和已经迁移到其他日期的航班不能被误删。
|
||||
5. 消息重放不得重复推进版本、重复生成业务事件或重复改变当前态。
|
||||
6. 合法的重复输入不得静默丢弃。容量不足、未知属性、非法结构必须显式失败并记录原因。
|
||||
7. 共享 MySQL 的成功回填属于提交后的补偿任务;回填失败不能把已成功的业务事务重新标记为失败。
|
||||
数据库:现场提供库时目标是 Oracle 11g;当前实际接通的只有 PostgreSQL,本文描述 PG 实现。
|
||||
|
||||
## 3. 逻辑模型
|
||||
## 2. 存储:一张主表加九张明细表
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
FLIGHT_SCHD ||--o{ FLIGHT_GATE : has
|
||||
FLIGHT_SCHD ||--o{ FLIGHT_CHECKIN : has
|
||||
FLIGHT_SCHD ||--o{ FLIGHT_BELT : has
|
||||
FLIGHT_SCHD ||--o{ FLIGHT_STAND_PLAN : has
|
||||
FLIGHT_SCHD ||--o{ FLIGHT_CHUTE : has
|
||||
FLIGHT_SCHD ||--o{ FLIGHT_DELAY : has
|
||||
FLIGHT_SCHD ||--o{ FLIGHT_BRIDGE_OP : records
|
||||
FLIGHT_SCHD ||--o{ FLIGHT_CHOCK_OP : records
|
||||
FLIGHT_SCHD ||--o{ FLIGHT_ROUTE_POINT : has
|
||||
SCHD_GEN ||--o{ SCHD_GEN_FLID : contains
|
||||
```
|
||||
### 为什么不用固定槽位
|
||||
|
||||
### 3.1 航班主表:`FLIGHT_SCHD`
|
||||
航班资源是变长集合:登机门通常一两个、值机柜台一到三个、转盘一个,报文规范理论上限 99。ACM2-29 曾定过"全标量宽表、零子表"方案(GATE1/GATE2 式固定槽位),当天撤销——槽位会截断数据,而设计要求保留重复资源的全部属性、输入顺序和源序号。结论:标量进主表,集合进明细表。
|
||||
|
||||
一行代表一个 `FLID` 的当前状态。保留现有 SIS 标量字段和派生字段,但不再把重复资源展开为 `GATE1/GATE2` 这类固定槽位。
|
||||
### 表清单
|
||||
|
||||
建议补充:
|
||||
|
||||
| 字段 | 含义 |
|
||||
| 表 | 作用 |
|
||||
|---|---|
|
||||
| `FLID` | 本地航班主键;单机场单 AODB 部署下可继续使用。 |
|
||||
| `FDAY` | DNLD 快照归属日,可为空;不等同于航班业务日期。 |
|
||||
| `LAST_MESSAGE_ID` | 最后影响该航班的消息 ID,用于审计和恢复判断。 |
|
||||
| `STATE_VERSION` | 有效状态变更版本;无副作用重放不得推进。 |
|
||||
| `SODT_RAW` 等 | 上游原始字符串,保证兼容和对拍。 |
|
||||
| `SCHEDULED_AT` 等 | 按机场业务时区解析后的查询字段。 |
|
||||
| `CREATED_AT`、`UPDATED_AT` | 本系统写入时间。 |
|
||||
| `FLIGHT_SCHD` | 主表,一行一个 FLID:标量字段、异常前缀列、SRVT/VIPF/MAFL_TEXT、FDAY、STATE_VERSION、LAST_MESSAGE_ID、时间戳。 |
|
||||
| 8 张明细表 | gate、checkin、belt、stand_plan、chute、delay、bridge_op、chock_op;主键 (FLID, ORDINAL)。 |
|
||||
| `FLIGHT_ROUTE_POINT` | 第 9 张明细表,ROUT/ERUT 两类路线共用,ROUTE_KIND 区分。 |
|
||||
| `SCHD_GEN` / `SCHD_GEN_FLID` | 日代版本、最后消息身份、当日成员集合;成员表带外键级联删除。 |
|
||||
| `PIPELINE_LOCK` | 单写者行锁,每个处理事务先锁 FLIGHT_SCHD_WRITER 行。 |
|
||||
| `BACKFILL_TODO` | 回填待办,见第 4 节。 |
|
||||
| `FLIGHT_SCHD_DISPLAY` | PG 展示视图(前两个登机门/柜台、首个转盘/延误、资源数),只是投影,不是状态来源。 |
|
||||
|
||||
时间解析必须保留原值。SIS 使用本地机场时间格式时,不能直接当作 UTC;机场时区来自配置,解析失败不能静默转换。
|
||||
9 张明细表承载 10 类集合——路线表一张顶两类。
|
||||
|
||||
### 3.2 重复资源表
|
||||
三个要知道的事实:
|
||||
|
||||
每个明细表至少保留以下通用字段:
|
||||
1. **明细表没有声明外键**,清理由仓储显式执行,不靠数据库级联;SOURCE_SEQ 可空且不唯一,只是记录——逐条定位更新的 Apply 协议因此不铺开(无生产调用方,定位键不可靠)。
|
||||
2. **差删按日期圈定范围**:先选出目标 FDAY 的实际成员,再删这些成员的明细和主行;跨日迁移和 FDAY 为空的航班不动。(这里曾有 bug:先删明细再圈主行,导致保留航班的集合被误删;已修复并有真实 PG 回归。)
|
||||
3. **演进纪律**:不为尚无消费者的查询加类型化时间列、摘要、额外载荷表或索引;时间先保留原始字符串,字段与长度以已应用迁移为准。
|
||||
|
||||
### 已知待修
|
||||
|
||||
路线表主键仍是 (FLID, ORDINAL):同一航班 ROUT 和 ERUT 各自从序号 1 写起,共存即冲突。计划用新迁移改成 (FLID, ROUTE_KIND, ORDINAL),见 ACM2-30。
|
||||
|
||||
## 3. 航班状态的两条来路:请求/下载与动态数据
|
||||
|
||||
状态只有两个来源,各成一条线:
|
||||
|
||||
- **请求/下载线(基准)**:建立某天航班的基准全量。本系统向 AODB 发日计划请求,AODB 回 SCHD 报文(一整天全部航班),快照整体落库;当天没再出现的航班被差删。
|
||||
- **动态线(运行事件)**:跟踪运行中的持续变化——实际时间、登机门开关、值机柜台、行李转盘、延误、靠撤桥、轮挡等。FLOP 类事件规范有 29 类,当前已接 GTDT 一类;一条报文只改一个航班的一类资源,其余字段不动。
|
||||
|
||||
两条线共用同一个入口和同一套事务/失败规则:进事务后都是"锁内读态 → 合并 → 写库 → 事件入 outbox → 终态 → 提交 → 回填"。
|
||||
|
||||
```text
|
||||
FLID 主表外键
|
||||
ORDINAL 本次集合中的输入顺序
|
||||
SOURCE_SEQ 源报文序号,如 GTNO/CKNO/ASNO/CSNO
|
||||
RECORD_VERSION 产生该记录的状态版本
|
||||
CREATED_AT
|
||||
UPDATED_AT
|
||||
信箱队头 → 解码 → 绑定身份(去重)→ 按 Handler 路由
|
||||
├─ SCHD-DNLD(已接通)────────→ 请求/下载线
|
||||
├─ SCHD-RESP / ADFT(未接通)─→ 请求/下载线
|
||||
└─ FLOP 事件(已接 GTDT)────→ 动态线
|
||||
```
|
||||
|
||||
业务字段按 SIS 原样保存。例如:
|
||||
### 公共入口(所有消息都走)
|
||||
|
||||
| 表 | 主要字段 |
|
||||
|---|---|
|
||||
| `FLIGHT_GATE` | `GATE`、`PGOT`、`PGCT`、`GOTM`、`GCTM`、`GTYP` |
|
||||
| `FLIGHT_CHECKIN` | `CHKC`、`CCLS`、`PCOT`、`PCCT`、`COTM`、`CCTM`、`CTYP` |
|
||||
| `FLIGHT_BELT` | `BELT`、`BCLS`、`PCOT`、`PCCT`、`FBAG`、`LBAG`、`BTYP` |
|
||||
| `FLIGHT_STAND_PLAN` | `PSST`、`STST`、`STET` |
|
||||
| `FLIGHT_CHUTE` | `CHUT`、等级、计划/实际时刻、类型字段 |
|
||||
| `FLIGHT_DELAY` | `CODE`、`STRT`、`DURA`、原文说明 |
|
||||
| `FLIGHT_BRIDGE_OP` | `ASNO`、`ABDG`、`ABOP`、`AOTM` |
|
||||
| `FLIGHT_CHOCK_OP` | `CSNO`、`CHID`、`CHST`、`CHTM` |
|
||||
| `FLIGHT_ROUTE_POINT` | `ROUTE_KIND`、`RTNO`、`APCD`、`SCAT`、`SCDT` |
|
||||
1. **排队**:信箱消息和定时任务共用一个 FIFO 队列,主泵只处理队头;队头失败按退避等待,重试超限或队头滞留超过时限 → DEAD。
|
||||
2. **解码**:报文非法 → DEAD 不重试;解码器缺陷类错误 → FAILED 退避重试。
|
||||
3. **绑定身份**:幂等键 = 发送方|类型|子类型|序号,仅首次处理时绑定;已被其他消息占用 → SKIPPED(重复),自身照常回填共享信箱。
|
||||
4. **路由**:按报文类型找 Handler;没有注册的 Handler → FAILED(可重放,不写终态)。
|
||||
|
||||
主键推荐为 `(FLID, ORDINAL)`。`SOURCE_SEQ` 是源数据,不默认唯一;相同资源号不代表同一条分配记录,因此不能按 `GATE`、`CHKC` 或 `ABDG` 全局去重。
|
||||
### 请求/下载线:基准全量怎么来
|
||||
|
||||
`FDIV`、`FRET`、`FLAB` 当前是 XSD 中的单值结构,可以保留在主表的明确前缀列中;必须定义显式清除语义。`SRVT`、`VIPF`、`MAFL` 如暂时不需要 SQL 查询,可使用专用集合载荷表保存原始文本;Oracle 11g 使用 `CLOB`,PostgreSQL 使用 `TEXT`,应用层负责结构校验。
|
||||
SIS 循环:本系统发日计划请求 → AODB 回 SCHD。SCHD 三个子类型载荷结构相同:
|
||||
|
||||
### 3.3 快照版本
|
||||
- **DNLD**:AODB 定时下发的时刻表下载(当前唯一接通的)。
|
||||
- **RESP**:应答本系统的日计划请求。
|
||||
- **ADFT**:临时/加班航班,单条航班记录。
|
||||
|
||||
```text
|
||||
SCHD_GEN
|
||||
FDAY 主键
|
||||
VERSION 单调递增版本
|
||||
LAST_MESSAGE_ID 生成该版本的消息
|
||||
CONTENT_DIGEST 可选,内容摘要
|
||||
UPDATED_AT
|
||||
请求登记在 REQ_TRACK(同类并发=1,新请求把旧请求强制置 EXPIRED),出站经 COUTMSGS outbox——发送与超时重发尚未接线。
|
||||
|
||||
SCHD_GEN_FLID
|
||||
FDAY
|
||||
FLID
|
||||
PRIMARY KEY (FDAY, FLID)
|
||||
```
|
||||
收到下载后的落库步骤:
|
||||
|
||||
`VERSION` 只是顺序令牌;重放判断必须结合 `LAST_MESSAGE_ID` 或明确的快照身份,不能只比较 `VERSION == expected + 1`。
|
||||
1. 解析整包并校验,单包上限 10000 个航班(超限 DEAD);真实解析未接通,测试靠注入 staging 数据。
|
||||
2. 进事务(先锁 PIPELINE_LOCK 行):
|
||||
- **重放短路**:该消息已提交过(日代 LAST_MESSAGE_ID 相同)→ 直接 SUCCEEDED,不加版本、不发事件。
|
||||
- **算成员差**:新快照里消失的航班进差删名单。
|
||||
- **合并写入**:锁内逐航班读当前态 → 合并 → 整集合替换,FDAY 置为快照日期。
|
||||
- **差删**:删差删名单航班的明细和主行(规则见第 2 节)。
|
||||
- **CAS 推进日代版本**:版本不符回滚 → FAILED(INFRA) 退避。
|
||||
- **应答匹配**:有待答的 SCHD 请求则标记完成。
|
||||
- 每个航班一条 KAFKA_SCHD 事件入 outbox;PROC_STATE → SUCCEEDED;提交。
|
||||
3. 提交后回填共享信箱(失败落 BACKFILL_TODO,见第 4 节);重放短路同样要回填。
|
||||
|
||||
### 3.4 处理和投递表
|
||||
现状:只有 DNLD 被路由进这条流程;RESP 和 ADFT 还没有 Handler,进来会记 FAILED(UNSUPPORTED)——"请求 → 应答 → 标记完成"的闭环待接通。参考数据请求(静态主数据 21 类)用同一张 REQ_TRACK,但应答落参考库、由 RequestCoordinator 处理,与航班状态无关。
|
||||
|
||||
保留 `PROC_STATE`、`MSG_EVENT`、`PUMP_JOB`、`REQ_TRACK` 等现有管道表。建议在 `MSG_EVENT` 中记录 `CMINMSGS_ID`、`STATE_VERSION` 和 `IDEMPOTENCY_KEY`,便于确认同一业务事件是否已经入队。
|
||||
### 动态线:运行事件怎么改状态(以 GTDT 为例)
|
||||
|
||||
## 4. 消息写入语义
|
||||
1. **Handler 纯函数决策**:输入 = 受影响航班的当前态 + 报文,输出 = 字段变更和待发事件;这一步不算账、不落库。每类资源的语义在 Handler 里固定,如 GTDT 是登机门集合整体替换(GTNO=0 表示清除)。
|
||||
2. 进事务(先锁 PIPELINE_LOCK 行):
|
||||
- 每个受影响航班:锁内重读最新态 → 合并字段变更 → 整集合替换写入,保留已有 FDAY。
|
||||
- 事件(KAFKA_MSG 通知 + KAFKA_SCHD 状态推送)入 outbox;PROC_STATE → SUCCEEDED;提交。
|
||||
3. 提交后回填共享信箱(同上)。
|
||||
|
||||
解码层把 XML 转换为显式命令,仓储层不再猜测字符串含义:
|
||||
### 状态变更的实现(两条线共用)
|
||||
|
||||
```text
|
||||
Unchanged 报文没有该字段,不修改数据库
|
||||
Set(value) 写入或覆盖值
|
||||
Clear 按协议删除该值
|
||||
Replace(list) 用完整集合替换旧集合
|
||||
Apply(item, key) 按协议中的源序号更新单条记录
|
||||
```
|
||||
所有状态变更都走同一套加工链:**报文 → 命令 → 合并出新状态 → 写库**。
|
||||
|
||||
通用规则:
|
||||
#### 数据的统一形态
|
||||
|
||||
- 外层标量字段默认是字段级增量合并:未出现不修改,出现则覆盖。
|
||||
- DNLD 对航班集合使用完整快照替换;集合缺失表示“按 DNLD 规则处理”,不能自动推广为清空所有旧资源。
|
||||
- 集合出现且被协议定义为快照时,在同一事务中删除该航班该集合的旧明细,再批量写入新明细。
|
||||
- `GTNO=0`、`CKNO=0` 等清除标记只对协议明确规定的集合生效。
|
||||
- 非法 JSON、未知字段、超长文本和超过协议容量的输入不得截断后成功。
|
||||
内存里一个航班的状态就是一个模型(FlightNextState):一组标量键值 + 10 类集合(每类是一组条目,每条目一组键值)+ 版本/消息追踪字段。主表存标量,明细表存集合,列映射固定(如 GTDT 的 GATE/GOTM/GCTM → flight_gate 对应列);读回时按 ORDINAL 组装集合,DB 权威态 = 主行 + 明细行。
|
||||
|
||||
成功提交后的出站载荷必须从同一份 `nextState` 生成,不能让仓储先丢字段、事件再使用原始输入。
|
||||
#### 第一步:报文变成命令
|
||||
|
||||
## 5. 事务流程
|
||||
`commandsFromFields` 把 Handler 解出的字段集翻成命令,规则:
|
||||
|
||||
```text
|
||||
解析并校验报文(锁外)
|
||||
↓
|
||||
取得 PIPELINE_LOCK 行锁
|
||||
↓
|
||||
复核消息状态和快照身份
|
||||
↓
|
||||
读取当前航班与 SCHD_GEN
|
||||
↓
|
||||
计算 nextState
|
||||
↓
|
||||
写主表、明细表、快照成员和版本
|
||||
↓
|
||||
从 nextState 生成 MSG_EVENT
|
||||
↓
|
||||
PROC_STATE = SUCCEEDED
|
||||
↓
|
||||
提交
|
||||
↓
|
||||
补偿回填共享 MySQL / 投递下游
|
||||
```
|
||||
- **字段没出现 = 不动**。增量报文只带变化的部分,其余不碰。
|
||||
- **标量出现 = Set**(覆盖)。异常键(FDIV/FRET/FLAB)载荷为空串、null、空对象时 = Clear。
|
||||
- **集合出现 = Replace**(整组替换为报文给的条目);整组条目序号属性为 0(如 GTNO=0)= Clear(显式清除);清除标记和正常条目混在一组直接拒绝,不猜。
|
||||
- DELY 没有协议序号属性,不支持逐条 Apply;清除走空数组替换。
|
||||
|
||||
CAS 失败直接回滚并进入可重试的 `FAILED(INFRA)`;不能用相同版本号推断“已经是我的提交”。
|
||||
#### 第二步:命令合并进当前态(纯函数)
|
||||
|
||||
提交之后的回填失败只创建或更新补偿记录。重启时先检查 `PROC_STATE` 和 outbox,已经成功的消息不能重新执行航班业务变更。
|
||||
`FlightStateEngine.apply` 从当前态拷贝一份,逐条应用命令:Set 覆盖键值,Clear 删键并记入 clearedKeys;Replace 整组换,Clear 删组。然后版本 +1、记 LAST_MESSAGE_ID,产出 FlightNextState。纯函数:不碰库、不发事件,同样输入永远同样输出。
|
||||
|
||||
## 6. PostgreSQL 与 Oracle 11g
|
||||
#### 第三步:新状态写进库(唯一写入口 persistNextStates)
|
||||
|
||||
两种数据库使用相同的仓储接口、领域模型和测试样例,分开提供迁移和 SQL 实现。
|
||||
- **主表标量**:快照线整行替换(FDAY=快照日期);动态线只 UPDATE 本次出现的列,clearedKeys 对应的物理列显式置 NULL,新航班先插一行(FDAY 为空)。
|
||||
- **明细集合**:每次写航班,9 张明细表对该 FLID 先 DELETE 再按合并后的集合整组 INSERT——即使某个集合本次没变也重写,保证库里与内存状态严格一致。这就是"整集合替换"的落地:ORDINAL 按输入顺序 1..N,SOURCE_SEQ 存协议序号,RECORD_VERSION 记写入时的版本。
|
||||
- last_message_id / state_version / updated_at 一并更新。
|
||||
|
||||
| 能力 | PostgreSQL | Oracle 11g |
|
||||
|---|---|---|
|
||||
| Upsert | `INSERT ... ON CONFLICT` | `MERGE` 或受控的 UPDATE/INSERT |
|
||||
| 长文本 | `TEXT` | `CLOB` |
|
||||
| 时间 | `TIMESTAMPTZ` | `TIMESTAMP` 配合明确时区转换策略 |
|
||||
| 自增 ID | identity/sequence | sequence |
|
||||
| 锁 | `SELECT ... FOR UPDATE` | `SELECT ... FOR UPDATE` |
|
||||
| 迁移 | PG 专用 Flyway location | Oracle 11g 专用 Flyway location,经目标版本实测 |
|
||||
#### 事件从状态序列化而来
|
||||
|
||||
Oracle 11g 的 `VARCHAR2` 受 4000 字节限制,空字符串按 `NULL` 处理。因此应用命令必须保留 presence/operation 信息,不能把“缺失、空串、NULL、清除”当作同一个状态。JDK 25、Oracle 11.2 补丁、JDBC 驱动、Flyway 和连接池的组合必须在现场环境实测,不能用 PostgreSQL 通过代替 Oracle 验收。
|
||||
KAFKA_SCHD 载荷 = FlightNextState 序列化(FlightFieldsJson):键按字典序输出保证同状态字节级稳定;集合键输出为真 JSON 数组/对象,不做双重字符串编码。快照线的事件与落库用同一份事务内状态;动态线的事件目前来自 Handler 锁外预览(见已知偏差)。
|
||||
|
||||
## 7. 迁移和发布
|
||||
### 失败规则(两条线共用)
|
||||
|
||||
1. 先增加明细表和主表追踪字段,不删除现有集合列。
|
||||
2. 从旧列、完整 DNLD 或可信历史输入回填明细表。
|
||||
3. 逐航班比较数量、顺序、源序号、属性、空值和出站载荷。
|
||||
4. 切换读路径和写路径到无损模型,保留旧结构一个回退窗口。
|
||||
5. 确认对拍、回放、备份恢复和查询性能通过后,再删除旧的损失性列和转换代码。
|
||||
事务内任何一步失败 → 整体回滚,消息记 FAILED 退避重试。已提交的成功不受后续失败影响:回填和投递失败只落补偿待办,不降级 SUCCEEDED,不重放业务。
|
||||
|
||||
已经执行过直接删除旧集合列的实例无法从新槽位列推回第三个登机门、原始序号或多次操作;需要旧库备份、完整 DNLD 或原始报文恢复。
|
||||
### 不变量(两条线共用)
|
||||
|
||||
## 8. 验收标准
|
||||
- **状态、事件、终态同事务**:库里能看到的必是完整提交结果。
|
||||
- **计算是合并不是覆盖**:报文没提的字段一律不动(加工链见上节)。快照更新 FDAY,动态线保留 FDAY,新行为空。
|
||||
- **快照重放只有一道防线**:LAST_MESSAGE_ID 识别最近一次快照的重放;更早的历史快照重入是否安全,靠人工重放的版本/业务日期策略。
|
||||
- 字段缺失、清除、空数组、异常对象的精确行为见语义矩阵。
|
||||
|
||||
- 三个及以上登机门、非连续源序号、相同资源号不同属性都能完整读回。
|
||||
- 多次靠桥/撤桥、上/下轮挡和多条延误记录不丢失顺序或资源身份。
|
||||
- 未出现、显式清空、集合替换、序号删除四种语义有独立测试。
|
||||
- 主表、明细、`SCHD_GEN`、outbox 和成功终态原子提交;任意一步失败全部回滚。
|
||||
- 同一快照重放不增加版本和事件;不同快照争用同一版本时只能一个成功。
|
||||
- 共享信箱回填失败不会重新执行业务状态变更。
|
||||
- PostgreSQL 与 Oracle 11g 各自通过新建库、升级库、回滚和并发锁测试。
|
||||
- 查询视图可以提供首个/第二个资源和总数,但完整明细仍可查询。
|
||||
- 对拍工具按字段、顺序、空值和集合内容比较,不通过忽略差异来“洗绿”。
|
||||
### 已知偏差(待收敛)
|
||||
|
||||
## 9. 实施顺序
|
||||
GTDT Handler 在事务外算一遍预览并构造事件,主泵在事务里再算一遍。目标是 Handler 只表达业务变化,事务内算出唯一一份 nextState,落库和 KAFKA_SCHD 共用——尚未实现。
|
||||
|
||||
| 阶段 | 内容 |
|
||||
|---|---|
|
||||
| P0 | 修复快照 CAS、提交后回填状态边界,建立 16 类结构的语义矩阵。 |
|
||||
| P1 | 建立无损明细表、领域命令和 nextState 生成;完成 DNLD 与一个 FLOP 纵向链路。 |
|
||||
| P2 | 完成旧数据回填、双读对拍和兼容查询视图。 |
|
||||
| P3 | 分别完成 PostgreSQL 和 Oracle 11g 的 DDL/DML、迁移和集成测试。 |
|
||||
| P4 | 根据真实查询计划增加索引,确认回退窗口关闭后删除旧损失性结构。 |
|
||||
## 4. 提交之后:回填与事件
|
||||
|
||||
本设计对应 ACM2-29 的后续实现;在评审确认前,不应将当前 V1.2.0 固定槽位结构作为生产保真方案。
|
||||
提交成功后有两件外部事:回填共享 MySQL 信箱、异步投递 Kafka,各自独立重试。
|
||||
|
||||
回填恢复靠一条持久待办:
|
||||
|
||||
- 现状:只在回填失败后写 `BACKFILL_TODO`;提交后立即崩溃的窗口没有待办,恢复不了。
|
||||
- 目标:在业务终态同一事务里登记回填意图,提交后回填成功再删待办。崩溃自然有账可查,不需要额外的"重启对账框架"。
|
||||
- 红线:登记待办再失败,不能把已提交的 SUCCEEDED 降级为 FAILED。
|
||||
|
||||
## 5. 怎么读
|
||||
|
||||
现在读一个航班 = 主行 1 次 + 10 类集合各 1 次,全量约 1+10N 次查询;多次读不在同一数据库快照,主子状态不保证一致。
|
||||
|
||||
后续在仓储边界按 FLID 批次加载明细,并明确一致性读事务;不引入缓存权威或通用查询框架。`GET /all/flights` 仍是 Controller TODO——仓储方法和展示视图存在不等于接口已交付。
|
||||
|
||||
KAFKA_SCHD 输出结构化 JSON,集合禁止双重编码成字符串。清除的 wire 表达、空数组、未知属性、异常局部替换仍有验收缺口,见核查报告;一个全字段样例通过不等于整个 SIS 协议无损。
|
||||
|
||||
## 6. Oracle:一整个工作项,不是"等环境"
|
||||
|
||||
PG 是唯一实现:SqlDialect 只覆盖两条主行 upsert;Oracle11gDialect 是没接线的模板(绑定顺序不兼容、MERGE 列数和占位符对不上);迁移目录只有 README,驱动、其余 SQL、视图、目标库测试都没写。
|
||||
|
||||
口径因此是"完整适配并验收":以一条跑通的仓储纵向链定接口,处理 DDL、绑定、空字符串、长文本,再上目标 11.2 实测。入口见 [Oracle 迁移说明](../src/main/resources/db/migration/oracle11g/README.md)。
|
||||
|
||||
## 7. 老数据升级:脚本能跑不等于数据无损
|
||||
|
||||
V1.1→V1.4:V1.2 删 *_TXT,V1.3 建空明细表,V1.4 删旧槽位列——旧集合数据没有任何自动恢复链路。带数据的实例升级前必须:导出/备份 → 选完整 DNLD 或可信原文恢复 → 逐字段核对后再切换。已发布迁移不改(不靠改历史隐藏损失),也别指望从已删槽位找回数据。
|
||||
|
||||
## 8. 现状
|
||||
|
||||
已验证:PG 上的事务、部分集合往返、CAS、行锁、脚本升级,101 个测试全绿。
|
||||
未完成:真实 DNLD/RESP 解析与生产路由、`/all/flights`、全字段与空值无损往返、提交后崩溃恢复、带数据无损迁移、查询规模证据、Oracle 实测、现场对拍。分项证据在核查报告与 Plane,本文不复述工单流水账。
|
||||
|
||||
Reference in New Issue
Block a user