refactor(storage): ACM2-12 落地——自有 PostgreSQL 全内部状态,共享 MySQL 仅信箱

按 ACM2-12 定案把仓库存储口径从"MySQL 六辅助表"推进到最终形态:
- 迁移:删除 MySQL V2.0.0 六表脚本,新增自有 PG V1.0.0
  (PROC_STATE/MSG_EVENT/PUMP_JOB/REQ_TRACK/REF_MASTER,PG 方言,REQ_TRACK.COUTMSGS_ID
  按 U18 修为 BIGINT;FLIGHT_STATE 阶段 B 缓做不建表)。
- 配置:datasources.default = 自有 PostgreSQL(enabled=false 待 U05);
  移除 datasources.reference/flyway.reference;新增 mailbox.shared-mysql(共享信箱,
  仅 DML,不建表);test profile 显式启用 H2 内存 datasource。
- 接口/注释:Repositories KDoc 按 ACM2-12 归属(自有 PG / 信箱封装 / gen→Redis 占位 /
  FLIGHT_STATE 缓做);InboxService、Pump 事务模型注释对正
  (信箱外部副作用 + PG 本地事务 + 回填最终一致)。
- 文档:architecture §1/§2/§3/§4/§5(D1/D2)/§6/§7/§8、design §1/§2/§3.1/§3.4/§3.5/§6/§9、
  README 全部按 ACM2-12 收口(自有 PG + 共享信箱 + Redis 动态/gen + 阶段 B 缓做)。

验证:37 测试全绿;dev stub 冒烟仍可启动、/health UP、收报 200。
信箱适配层(CminmsgMailbox/OutboxMailbox)、gen→Redis Lua、作业窗口语义、影子重设计
属 ACM2-12 Checks ②③④⑤(U05/U09 批次)。
This commit is contained in:
windyboy
2026-09-07 08:21:11 +08:00
parent a3f1da4bcc
commit e0ce863905
10 changed files with 280 additions and 235 deletions
+57 -51
View File
@@ -10,7 +10,7 @@
```
ProcStatusPROC_STATE.STATE,消息处理侧):
PENDING ──处理成功──▶ SUCCEEDED(终态回填 CMINMSGS
PENDING ──处理成功──▶ SUCCEEDED(终态回填共享库 CMINMSGS 为外部副作用,最终一致
│ ──同 identity 已绑定──▶ SKIPPED(终态,lastError=duplicate-of:<id>
└──失败──▶ FAILED(非终态,attempts+1 + nextAttemptAt 退避)
│ attempts ≥ maxAttempts 或 队头滞留超 head-deadline
@@ -46,42 +46,45 @@ ErrorClass(两侧共用):
schdPush / outboundIntents / refUpserts),不触碰 Redis/Kafka——副作用全部由泵边界执行。
- Handler 实装:0/32(骨架),翻译属阶段 2/3,逐条对照 ACM2-4 行为基线与 KEEP/FIX 矩阵。
## 2. 数据模型(六辅助表 + 静态参考库
## 2. 数据模型(自有 PostgreSQL · ACM2-12
`db/migration/V2.0.0__aux_tables.sql`(legacy 旧表不在本仓库声明,见 architecture.md §6):
`db/migration/V1.0.0__own_pg_pipeline.sql`(PG 方言,自有库;legacy 旧表与共享库表不在
本仓库声明,见 architecture.md §6):
| 表 | 角色 | 关键列/约束 |
|---|---|---|
| PROC_STATE | 消息处理伴生状态(不动 CMINMSGS 旧列) | `UK_PROC_IDENTITY(IDENTITY_KEY)` 唯一约束=I3 依据;`IDX_PROC_HEAD(STATE, CMINMSGS_ID)`=队头查询 |
| MSG_EVENT | 统一投递 outbox | `EVENT_ID` 自增=全序;`IDX_EVT_HEAD(TARGET, STATE, EVENT_ID)`=每 target 队头 |
| REF_DATA | **SCHD_GEN(流程 4 gen 协议,留守业务库)**;原 21 类静态数据已拆分迁独立 PG 参考库(ACM2-11) | `VERSION` 列支撑流程 4 CASgen 行) |
| REQ_TRACK | 15 类请求状态机 | REGISTERED/SENT/WAITING/DONE/EXPIRED |
| PUMP_JOB | 泵作业队列 | kindARCHIVE/HISTORY_SWEEP/PROJECTION_REBUILD |
| FLIGHT_STATE | 阶段 B 权威 | `replaceDay` 单事务删差集+写新代+版本提升 |
| PROC_STATE | 消息侧:处理伴生状态/重试/毒丸(与共享库 CMINMSGS_ID 对应) | `uk_proc_identity(IDENTITY_KEY)` 唯一约束=I3 依据;`idx_proc_head(STATE, CMINMSGS_ID)`=队头 |
| MSG_EVENT | 发消息侧:统一投递 outbox | `EVENT_ID` 自增=全序;`idx_evt_head(TARGET, STATE, EVENT_ID)`=每 target 队头 |
| PUMP_JOB | 泵作业调度(作业不插队,队头空闲/退避窗口执行) | kindARCHIVE/HISTORY_SWEEP/PROJECTION_REBUILD |
| REQ_TRACK | 15 类请求状态机 | REGISTERED/SENT/WAITING/DONE/EXPIRED`COUTMSGS_ID BIGINT`U18 修正) |
| REF_MASTER | 21 类静态主数据 | `(RTYPE,RKEY)` PKSOURCE=ADMINAPI/AODB/PIPELINEREFRESHED_AT |
已知 DDL 缺口(U18未修):`REQ_TRACK.COUTMSGS_ID` 应 INT→BIGINT`FLIGHT_STATE`
主键应含 FDAY;时间列 TIMESTAMP(秒级+会话时区)应 DATETIME(6)/显式 UTC,否则退避/毒丸
判定存在系统性偏移风险。
已知 DDL 缺口(U18随本库 PG 化修正/收窄):REQ_TRACK.COUTMSGS_ID 已按 BIGINT
时间列须 DATETIME(6)/显式 UTC 口径在 U05 数据层实现时定;FLIGHT_STATE 因缓做不在本库。
**边界(ACM2-112026-09-07 定案)**
- 业务事务库(MySQL `cdairport`):PROC_STATE / MSG_EVENT / REQ_TRACK / PUMP_JOB /
FLIGHT_STATE + `REF_DATA`**SCHD_GEN 行**gen CAS 与 SUCCEEDED 同事务,快照协议依赖,不能随迁);
- **21 类静态主数据 → 独立 PostgreSQL 参考库**`datasources.reference``StaticRefRepository`
SOURCE=ADMINAPI/AODB/PIPELINE):写少读多、外部来源、弱事务;Redis 只作只读热点投影
legacy orms_stand 语义延续,阶段 6);
- 航班动态权威 Redis(阶段 A flightInfo**不变**。
- 实现状态:Repository 已拆(`RefDataRepository`=gen-only / `StaticRefRepository`=21 类);
reference datasource 已占位(enabled=false);表结构草案、迁移 SQL、ReferenceService 21 类
同步与 codec 应答接线属阶段 6(U05 数据层批次后)。
**存储边界(ACM2-12 定案)**
- **自有 PostgreSQL** = 上表全部(消息管道 + 调度 + 请求 + 21 类)。本地事务只在此库:
处理侧「MSG_EVENT 插入 + PROC_STATE→SUCCEEDED同事务;其余跨存储一律外部副作用。
- **共享 MySQLcdairport,他人系统)仅信箱 DML、不建表**:收报写 CMINMSGS、出站写
COUTMSGS(他人读取发送)。见 §3.1/§3.2 的事务模型。
- **Redis**:航班动态 flightInfo + 快照 **SCHD_GENgen**——Lua 内原子「覆盖+按代差删+
版本推进」;重放幂等由 Lua 承接(协议重设计属 U09),`RefDataRepository` 为目标实现的
过渡占位接口。
- **FLIGHT_STATE(阶段 B 权威):缓做不落表**(Redis 永续动态权威)。
- 实现状态:迁移 SQL 已按 PG 落地(V1.0.0);Repository 接口归属注释已对正(自有 PG /
信箱封装 / gen→Redis 占位 / FlightState 缓做);Micronaut Data 实装与信箱适配层
CminmsgMailbox/OutboxMailbox)属 U05 批次。
## 3. 核心流程设计
### 3.1 流程 1:收报(`InboxService.accept`
事务 1 = `insertRaw`CMINMSGS 原文)+ `procState.insert`(伴生 PENDING 行);
不解析报文、接收层无唯一约束(I3)。响应 = 记录 ID(「已持久化」语义,与现役逐字对拍
后固化,U16)。`wakePump()` 目前为 TODO 空操作——泵 1s 轮询兜底,唤醒仅为加速。
事务边界随 U05@Transactional + allopen)补齐。
收报 = 共享信箱外部写 + 自有 PG 入队(ACM2-12,跨库,非同一事务):
1. `insertRaw`:共享 MySQL CMINMSGS 落信成功(返回其主键 CMINMSGS_ID)——「已持久化」
语义即刻成立,响应可返回(与现役逐字对拍后固化,U16);
2. `procState.insert(id)`:自有 PG 建 PENDING 行入队;本步失败 → 补偿(以共享库
`DATE_PROCESSED IS NULL` 重扫补建,U05 批次实现);
3. 不解析报文、接收层无唯一约束(I3)。`wakePump()` 为 TODO 空操作——泵 1s 轮询兜底。
### 3.2 流程 2:主泵 tick`Pump.tick`
@@ -102,8 +105,10 @@ ErrorClass(两侧共用):
decodeMALFORMED→DEAD / CODEC_ERROR→FAILED)→ identity 首绑
`tryBindIdentity` 失败 → SKIPPEDI3)→ Schd DNLD → `SnapshotFlow`
其余 → `Handler.decide(redis.hgetAllFlightInfo(), msg)`
阶段 ARedis 先写(I2 happens-beforeTODO redisApply)→ 事务 2
MSG_EVENT 插入 + CMINMSGS 回填 + SUCCEEDED。
阶段 ARedis 先写(I2 happens-beforeTODO redisApply)→ 自有 PG 事务 2
MSG_EVENT 插入 + PROC_STATE→SUCCEEDED(同库原子,@Transactional);
CMINMSGS 回填(DATE_PROCESSED/STATUS)为共享信箱**外部回填**:PG 提交后异步/补偿执行,
失败重试+告警(最终一致,ACM2-12;回滚接管语义以回填成功为界,需 Runbook 演练确认)。
5. 异常边界(U08):`processOne` 内 try/catch → `ProcFailure.fail(INFRA)`attempts+1、
退避、达上限 DEAD);`InterruptedException` 恢复中断位后**上抛**loop 仅 catch
`Exception` 作最后防线,`Error` 任其终止进程(异常必可见)。
@@ -120,32 +125,32 @@ ErrorClass(两侧共用):
claim、`lastFlush` 仅成功后推进;达上限整批 DEAD(DLQ)。周期/批上限取参数表
3s / 500)。
- 轮询间隔取参数表(下限 50ms,N18);无 200ms 硬编码。
- 阶段 B(定案 2/D2):ES 投递成功 → 同线程同步 `insertSync` 删除事件
`deleteOf`Jackson 结构化序列化,refs 可空恒合法 JSON——U14)。
- 阶段 B(定案 2/D2ACM2-12 缓做):ES 投递成功 → 同线程同步 `insertSync` 删除事件
`deleteOf`Jackson 结构化序列化,refs 可空恒合法 JSON——U14)。当前不启用。
### 3.4 流程 4:日计划快照(`SnapshotFlow`
```
staging(流式解析+整包校验,TODO 阶段2;未实装→FAILED(UNSUPPORTED)
→ Redis Lua SNAPSHOT_REPLACE(同一 hash 原子「覆盖新代+按代差删」,删除集=旧代flids−新代)
putGenIfVersion CASversion 未变才写;CAS 后重放=version 已达标→no-op 成功)
→ SUCCEEDED
CAS 冲突 → FAILED(INFRA)+退避(串行泵下不应发生→告警语义)
gen 版本推进(ACM2-12gen 随 flightInfo 同在 RedisLua 内原子版本 CAS——
目标实现;现 RefDataRepository/putGenIfVersion 为过渡占位)
→ 自有 PG SUCCEEDED
```
**已知缺口(U09,未定案**:Lua 与 CAS 分属两存储,非同一事务;崩溃窗口
Lua 后/CAS 前、CAS 后/SUCCEEDED 前)与幂等重放判据(版本不二次自增)的显式恢复协议
待定案并补测试。**实现注**:代码亦未实现「重放 no-op」——CAS 已提交而 SUCCEEDED 未写时,
重放会重新 `getGen` 到已达标版本并再次 CAS 成功(版本 1→2 二次自增),与「版本不二次自增」
注释不符(今日因 staging 未实装不可达,阶段 2 接 staging 后即成真缺陷)。
**已知缺口(U09,未定案ACM2-12 后重设计为 Redis 内协议)**gen 与 Lua/SUCCEEDED 不再
分属两存储即可同原子(全部在 Redis Lua);真正跨存储的窗口收窄为「Lua 已完成、PG SUCCEEDED
未写」——重放判据(版本不二次自增)与按代差删在 Lua 内以版本 CAS 承接,恢复协议待定案并补
测试。现有代码的「CAS 重放二次自增」缺陷(版本 1→2)与实现注随协议重设计一并消除。
### 3.5 泵作业(`JobExecutor`,经 PUMP_JOB 同队列
### 3.5 泵作业(`JobExecutor`PUMP_JOB 自有 PG,作业窗口执行
- HISTORY_SWEEP3:30 清场,I4 同步链):判史 → 同步写 ES → 仅删成功集。
**占位门禁(U10/T07 修订)**ES saveSync 接线前 `pickHistory` 恒空集、删除量恒 0
禁止「全量可删」fail-open 默认;现役五条判史规则 golden 通过后才允许接线。
- ARCHIVE3:00):1 天前且仅终态(SUCCEEDED/SKIPPED/DEAD迁 CMINMSGS_HSTTODO)。
- PROJECTION_REBUILD(阶段 B 切入时全量重建,TODO activeDays)。
- ARCHIVE3:00):1 天前且仅终态(SUCCEEDED/SKIPPED/DEAD)迁 CMINMSGS_HST共享库,
外部副作用;TODO U05 批次)。
- PROJECTION_REBUILD(阶段 B 缓做,ACM2-12;重新评估后再启用)。
## 4. 失败与重试统一设计(U08)
@@ -184,10 +189,10 @@ CAS 冲突 → FAILED(INFRA)+退避(串行泵下不应发生→告警语义)
| `identity.include-day-boundary` | false | 幂等键日边界(CONFIRM 前禁开) |
| `consistency-check.on-startup` / `daily-sample-ratio` | true / 0.01 | 一致性哨兵(实装属 U25) |
基础设施键位口径(Micronaut 5.1U03):`datasources.default.*`业务 MySQL)、
`datasources.reference.*`21 类静态 PGACM2-11enabled=false 待阶段 6)、
`flyway.datasources.default.*` / `flyway.datasources.reference.*``kafka.producers.default.*`
`eureka.client.*`logback 独立于本文件,环境变量前缀 `MSGX_LOGSTASH_*`
基础设施键位口径(Micronaut 5.1U03):`datasources.default.*`**自有 PostgreSQL**
ACM2-12)、`flyway.datasources.default.*``mailbox.shared-mysql.*`(共享信箱,仅 DML)、
`kafka.producers.default.*``eureka.client.*`logback 独立于本文件,
环境变量前缀 `MSGX_LOGSTASH_*`
## 7. 测试策略
@@ -215,16 +220,17 @@ CAS 冲突 → FAILED(INFRA)+退避(串行泵下不应发生→告警语义)
| 项 | 缺口 | 计划 |
|---|---|---|
| U05 | 仓储接口无实装`@Transactional`/allopen 未引入 → 生产 DI 装配失败、事务1/2 未原子 | 阶段 1Micronaut Data JDBC + allopen + 事务生效回归) |
| U05 | 自有 PG 数据层无实装(PROC_STATE/MSG_EVENT/PUMP_JOB/REQ_TRACK/REF_MASTER + 信箱适配层 CminmsgMailbox/OutboxMailbox`@Transactional`/allopen 未引入 → 生产 DI 装配失败 | U05 批次Micronaut Data JDBC on PG + allopen + 信箱外部副作用与补偿回归) |
| U07/U26 | `autostart` 默认关=有意门禁,但生产无 fail-fast;双实例无运行期防护 | fail-fast 定案 + 租约/DB 锁拒启 |
| U09 | 快照跨存储恢复协议未定案(含崩溃重放二次自增版本未实现 no-op,见 §3.4 注) | 崩溃窗口清单 + 幂等判据 + 测试 |
| U09 | gen→Redis 协议未重设计(Lua 内原子版本推进;崩溃窗口=「Lua 完成/PG SUCCEEDED 未写」) | Redis 内版本 CAS + 恢复协议 + 测试(ACM2-12 |
| U13 | 投递侧无 createdAt/headDeadline 超时升级、无 DEAD 告警出口;处理侧 head-deadline 判据亦不可达(见 §3.2 注) | WP2 |
| U15 | job 与队头消息无统一全序(入队时间近似) | 统一序号列定案 |
| U15 | job 与队头消息无统一全序(入队时间近似)——ACM2-12 后改口径:作业窗口执行,不追求与消息全序 | 作业窗口语义定稿(ACM2-12 Checks ④) |
| U16 | `/cminmsgs/send`@Consumes/字符集(实测 text/plain 415)、无错误路径契约(@ControllerAdvice | WP2legacy 逐字对拍固化) |
| U17 | Eureka 注册名仍取 `micronaut.application.name`=msgexchange-nextgen);`msgx.service-name` 无运行时消费方 → 影子/切流前注册名与文档契约脱节 | `micronaut.application.name=${msgx.service-name}`application.yml:9/30 |
| U18 | §2 所列 DDL 缺口 | WP2 |
| U19–U21 | 请求状态机量纲/死分支、identity 绑定静默跳过、REF_DATA 接口/DDL 对齐 | WP2 |
| U17 | Eureka 注册名仍取 `micronaut.application.name`=msgexchange-nextgen);`msgx.service-name` 无运行时消费方 → 影子/切流前注册名与文档契约脱节 | `micronaut.application.name=${msgx.service-name}`application.yml |
| U18 | DDL 缺口(随 PG 化收窄:REQ_TRACK.COUTMSGS_ID 已 BIGINT;时间列口径 U05 定) | U05 批次 |
| U19–U21 | 请求状态机量纲/死分支、identity 绑定静默跳过、21 类静态接口/表对齐(REF_MASTER/SOURCE | WP2 |
| U22U24 | 载荷类型收敛、eventSeq 未接线、每报文全量读语义定案 | WP3 |
| U25/U28/U30 | 一致性哨兵实装、README 安全节/入口、索引与杂项 | WP3/4 |
| U27 | 专有材料(SIS md 703KB / XSD 版权头)治理决策 | WP4(ACL 核验先行) |
| ACM2-11/12 | 存储边界(自有 PG + 共享信箱 + Redis 动态/gen + 阶段 B 缓做):迁移 SQL/配置/接口注释已按定案调整(V1.0.0 PG);信箱适配层、gen Lua、作业窗口语义、影子重设计未实装 | ACM2-12 Checks ①–⑥ |
| ACM2-11 | 21 类静态主数据独立 PG 参考库(datasources.reference):Repository 接口已拆(RefDataRepository=gen-only / StaticRefRepository),表结构/迁移/SOURCE 审计/ReferenceService 同步与应答接线未实装 | 阶段 6(U05 批次后);生产 Oracle 仅可能性,触发条件见 ACM2-11 |