feat(processing): 实现自有 PostgreSQL 运营航班权威存储与单事务闭环 (ACM2-28)
- FS1: 增加 Flyway 迁移 V1.1.0__flight_schd.sql,创建 FLIGHT_SCHD 与 SCHD_GEN - FS2: 实现 FlightSchdRepository 接口及 JdbcFlightSchdRepository 与 StubFlightSchd,增强 JdbcOps 事务管理 - FS3: 扩展 MessageProcessor 事务 2 与按 FLID 点查视图,合并变更、事件与终态入单事务提交 - FS4: SnapshotFlow SQL 化(批处理 upsert、域内差删、SQL CAS 推进与熔断保护),JobExecutor 接入 PG 清场删除 - FS5: 彻底退役 Redis 权威与写路径,移除 FlightRedisClient、Lua 脚本、健康指示器与配置残留 - FS6: 补齐 U09/U29 不变量门禁(崩溃幂等、CAS 防并发、ADFT 存活保障、非 UTC JVM/会话时区无漂移)与 FlywayMigrationTest - FS7: 交付影子对拍比较内核 FlightStoreDiffTool 与单元测试 - FS8: 全面回改 decision-flight-state、architecture、design、user-stories 权威文档与规范
This commit is contained in:
+16
-17
@@ -4,7 +4,7 @@
|
||||
|
||||
本文说明模块如何协作、状态如何流转,以及失败后如何恢复。系统范围、存储归属和部署约束见 [architecture.md](architecture.md),不在这里重复。
|
||||
|
||||
以下流程是阶段 A 的目标设计,不是实现完成清单。当前代码仍有占位和过渡实现,与设计的主要差异集中在第 10 节。阶段 B 的历史投影和清场暂不启用,也不改变 Redis 作为航班动态权威存储的定位。
|
||||
阶段 A 采纳 ACM2-28 选项 C 定案:运营航班权威状态落自有 PostgreSQL(表 `FLIGHT_SCHD` 与 `SCHD_GEN`),Redis 彻底退出动态权威与全部写路径。阶段 B 的历史投影和清场暂不启用。
|
||||
|
||||
## 2. 数据与领域模型
|
||||
|
||||
@@ -19,12 +19,13 @@
|
||||
| `PUMP_JOB` | 持久化维护作业 | 状态为 `QUEUED / RUNNING / DONE / FAILED`;不与业务消息共用排序序号。 |
|
||||
| `REQ_TRACK` | 上游请求及应答关联 | 保存请求类型、参数、出站信箱 ID、发送和完成时间;同类只允许一个开放请求。 |
|
||||
| `REF_MASTER` | 静态参考数据 | `(RTYPE, RKEY)` 唯一,`SOURCE` 记录数据来源。 |
|
||||
| `FLIGHT_SCHD` | 运营航班当前权威状态(SCHD 快照 + FLOP/ADFT 增量合并) | `FLID` 主键,`FDAY` 所属日代(可空),`FLTR_JSON` 全量,`TIMESTAMPTZ` 时间戳。 |
|
||||
| `SCHD_GEN` | 日计划代版本与当前代有效航班集合(差删依据) | `FDAY` 主键,`VERSION` 版本号,`FLIDS_JSON` 航班集合,`TIMESTAMPTZ`。 |
|
||||
| `PROC_STATE_HST` | 终态处理记录的归档目标 | 属于目标设计,当前迁移尚未建表;不得改写为共享库历史表。 |
|
||||
|
||||
字段与索引定义以 `src/main/resources/db/migration/` 为准。报文原文仍从共享信箱读取,因此必须协调原文保留期,不能在消息尚需处理或重放时提前清理。
|
||||
|
||||
Redis 保存 `flightInfo` 与快照代际元数据 `gen`。`gen` 记录快照所属日期、版本及该代航班集合,不属于静态参考数据。
|
||||
字段与索引定义以 `src/main/resources/db/migration/` 为准(含 `V1.1.0__flight_schd.sql`)。报文原文仍从共享信箱读取,因此必须协调原文保留期,不能在消息尚需处理或重放时提前清理。
|
||||
|
||||
Redis 已彻底退出动态权威与写路径;`FLIGHT_SCHD` 与 `SCHD_GEN` 在自有 PG 中由主泵单线程独占写入。
|
||||
### 2.2 消息、身份与决策
|
||||
|
||||
`XmlCodec` 将 XML 解码为 `DecodedMessage`,包含 `SNDR / TYPE / STYP / SEQN / DTTM` 元数据和业务载荷。`MsgKind` 区分 `SCHD` 与 `FLOP` 子类型;Handler 查找与日志类型标识使用同一套映射。
|
||||
@@ -44,7 +45,7 @@ Handler.decide(flightView, message) → Decision
|
||||
Decision = 航班变更 + msg 通知 + schd 状态 + 出站意图 + 静态数据变更
|
||||
```
|
||||
|
||||
Handler 不写 Redis、Kafka 或数据库。主泵负责应用决策;各类变更的持久化与重试边界必须明确,不能把“返回了 Decision”当成副作用已执行。
|
||||
Handler 不写 Kafka 或数据库。主泵负责应用决策;各类变更在自有 PG 单事务内提交。
|
||||
|
||||
### 2.3 状态与错误分类
|
||||
|
||||
@@ -97,16 +98,14 @@ Handler 不写 Redis、Kafka 或数据库。主泵负责应用决策;各类变
|
||||
├─ RESP / DNLD:快照流程
|
||||
└─ 其他:Handler 决策
|
||||
↓
|
||||
Redis 应用变更
|
||||
↓
|
||||
PG 本地事务:待发事件 + 处理终态
|
||||
PG 单事务:FLIGHT_SCHD 增量更新 + 待发事件 + 处理终态
|
||||
↓
|
||||
提交后补偿回填信箱
|
||||
```
|
||||
|
||||
- 原文缺失当前归为 `MALFORMED`;读取异常不能伪装成“缺失”,应进入基础设施重试。
|
||||
- 忽略规则覆盖约定的 `LDM / REGN / RSTA / EROR`,转 `SKIPPED` 并审计;不能产生业务副作用。
|
||||
- Redis 更新必须先于对应事件提交。PG 事务只覆盖本库,不能靠事务注解把 Redis 或 MySQL 操作变成原子操作。
|
||||
- 航班变更(`FLIGHT_SCHD`)与待发事件、处理终态必须在同一 PG 事务原子提交。跨存储双写窗口已根除。
|
||||
- 所有终态都需要回填信箱,包括成功、忽略、重复和死信;非终态禁止回填。回填必须在 PG 提交后执行,并有持久化补偿、退避与告警;影子环境禁写。
|
||||
|
||||
## 4. 日计划快照与请求匹配
|
||||
@@ -117,10 +116,10 @@ Handler 不写 Redis、Kafka 或数据库。主泵负责应用决策;各类变
|
||||
|
||||
1. **暂存校验**:流式解析后完成整包校验和航班规范化;失败前不修改权威状态。暂存数据可在崩溃后从原文重建。
|
||||
2. **应答守卫**:`RESP` 必须匹配开放的 `RQFD` 请求;无匹配、已过期或报文时间早于发送时间时,转 `SKIPPED` 并审计,不更新快照。
|
||||
3. **原子替换**:Redis Lua 在同一次操作中校验版本、覆盖新代、删除旧代差集并推进 `gen`。删除集为“旧代航班集合 − 新代航班集合”,不是全部现存航班,不能误删快照集合外的增量航班。
|
||||
4. **提交结果**:在 PG 同一事务中保存 `MSG_EVENT`、将消息置为 `SUCCEEDED`,并将匹配 `RESP` 的请求置为 `DONE`;随后执行信箱回填。
|
||||
3. **SQL 原子替换**:在自有 PG 单事务内批处理写入新代 `FLIGHT_SCHD` 全量、按 `FDAY` 域内差删旧代航班,并通过 SQL CAS 推进 `SCHD_GEN` 版本。删除集为“旧代航班集合 − 新代航班集合”,仅作用于属于该代的记录,ADFT(FDAY=NULL)与已迁移航班天然存活。
|
||||
4. **提交结果**:在同一 PG 事务中保存 `MSG_EVENT`、将消息置为 `SUCCEEDED`,并将匹配 `RESP` 的请求置为 `DONE`;事务提交后执行信箱回填。
|
||||
|
||||
关键恢复窗口是“Redis 已替换,PG 尚未提交”。重试同一快照必须识别已应用结果,不能再次增加版本,也不能用旧快照覆盖新状态。**版本校验与重放识别协议仍待实现验证**,单纯“读当前版本再加一”不满足要求。
|
||||
单事务保证崩溃后整体回滚,无跨存储中间态;重放时识别版本,绝不发生二次自增。
|
||||
|
||||
### 4.2 上游请求与静态数据
|
||||
|
||||
@@ -194,13 +193,13 @@ REGISTERED → SENT → WAITING → DONE
|
||||
| 重复扫描、入队中断、较小 ID 迟到 | 不重复入队、不丢记录、不让后续消息越序。 |
|
||||
| 队头失败、退避及作业竞争 | 消息不越队;到期后恢复;作业不使消息无限饥饿。 |
|
||||
| 同身份多条记录、失败后重试、归档后重复 | 只产生一次有效业务处理,不把自身重试判为重复。 |
|
||||
| Redis 成功后 PG 失败、快照重复或迟到 | 不重复推进版本、不回退状态、不误删增量航班。 |
|
||||
| PG 事务失败、快照重复或迟到 | 整体回滚重试、不重复推进版本、不回退状态、不误删增量航班。 |
|
||||
| PG 提交失败、信箱回填失败 | 事件与处理结果一起回滚;已提交结果只补偿回填。 |
|
||||
| 投递确认丢失、批次失败、次数耗尽 | 允许可识别的重发、保持目标顺序、整批退避并保留死信。 |
|
||||
| 请求超时、无匹配 RESP、时间单位不一致 | 不误用迟到应答,不提前完成请求。 |
|
||||
| stub 误配置、重复实例、停机中断 | 生产拒绝不安全启动,工作线程能正确退出。 |
|
||||
|
||||
真实适配器还需 PG/MySQL 事务与补偿集成测试、Redis Lua 中断恢复测试、Kafka 故障投递验证;启动冒烟只证明装配可用,不证明这些一致性要求已满足。常规验证命令为 JDK 25 下执行 `./gradlew test`。
|
||||
真实适配器还需 PG 事务与补偿集成测试、快照 SQL CAS 崩溃恢复测试、Kafka 故障投递验证;常规验证命令为 JDK 25 下执行 `./gradlew test`。
|
||||
|
||||
## 9. 实现入口
|
||||
|
||||
@@ -219,10 +218,10 @@ REGISTERED → SENT → WAITING → DONE
|
||||
|
||||
以下缺口直接影响上述设计是否成立,不能以类或接口已存在作为完成依据:
|
||||
|
||||
- **事务与外部副作用**:当前 JDBC 操作仍分散执行;普通处理按“插事件 → 同步回填信箱 → 更新状态”调用,尚未实现要求的 PG 原子提交与提交后补偿。Redis 普通变更、出站意图及真实出站信箱也未形成完整链路。
|
||||
- **事务与外部副作用**:ACM2-28 定案后,已完成 `FLIGHT_SCHD` 增量更新与快照全量写入、待发事件与 `PROC_STATE` 在自有 PG 单事务原子提交,提交后异步回填信箱。
|
||||
- **收报与调度**:轮询仍从 `afterId=0` 扫描,持久水位与补扫策略未完成;作业以“队头为 FAILED”近似窗口,未区分是否已到期。主泵直取系统时间,滞留判据使用更新时刻,不能保证设计要求的超时升级。
|
||||
- **快照与业务能力**:快照暂存仍为占位,当前分流仅覆盖 DNLD,RESP 守卫与请求完成事务未接通;`gen` 仍走过渡仓储,未实现 Redis 内原子版本与重放协议。忽略规则及所需 Handler 还需补齐。
|
||||
- **快照与业务能力**:快照 SQL 事务化已落地,支持 JDBC 批处理、域内差删与 SQL CAS 推进,超限 10000 熔断保护;忽略规则及所需 Handler 仍需在后续阶段铺开。
|
||||
- **请求、静态数据与归档**:请求在真实出站前就标记发送,时间匹配与审计仍需修正;静态数据存在进程内过渡实现,归档表与关联保留策略尚未落地。
|
||||
- **生产与运维**:缺少完整启动校验、运行期单写者保护、影子隔离和 Kafka 强制配置校验;投递滞留升级、死信告警、端到端追踪与指标未闭环。
|
||||
- **生产与运维**:缺少完整启动校验、运行期单写者保护、影子隔离和 Kafka 强制配置校验;投递滞留升级、死信告警、端到端追踪与指标未闭环。影子对拍 cross-store diff 工具已就绪。
|
||||
|
||||
进度与验收项见 Plane ACM2-10 实施计划(U01–U30),业务契约与待确认事项见 [user-stories.md](user-stories.md)。本文件不维护工单流水账、测试数量或历史方案全文。
|
||||
|
||||
Reference in New Issue
Block a user