docs: 同步信箱生命周期实现口径——§4 缺口收敛、水位空洞老化、回填事实并入 PROC_STATE
message-lifecycle.md:§4 两个崩溃窗口随"终态与回填意图同体同行"消除(不再是缺口,改为 说明剩余未闭环项是回填跨库单写期间的持续失败);§5.1 补空洞老化规则(自增回滚空位会使 水位永久停摆,超出最大提交时延即判永久并放行,只跳过空洞不越过已存在的行);§5.2 补判据 机制(不依赖独立待办表,R 覆盖退避);§3 表格改回填意图与"到期或已达 NOW − R"。 design.md:§2.1 表以 INBOX_CURSOR 取代 BACKFILL_TODO、PROC_STATE 责任补回填事实; §3.1 收报改为按 ID 区间扫描且不以标记为谓词;§3.3 终态由处理器事务内落库、死信同样可 补写;§6.1 维护作业改为 BackfillService.sweep;§7 补 max-commit-delay/overdue-backfill/ backfill-batch 与 mailbox.processed-value;§8 验证表补"终态未回填不得阻断发现"与"意图随 事务回滚";§9 入口表更新;§10 缺口两则改写为现状与待确认项。 architecture.md:§4 主流程第 1/3/4 步与 §3 存储表同步(水位、回填意图、单事务范围)。 flight-state.md:§2 对象表同步。user-stories.md:US-01/US-09 当前基础与落点改为实现现状、 Q2 口径改为"老化阈值待库方书面承诺"。
This commit is contained in:
@@ -62,10 +62,10 @@ CIIMS / AODB 等上游
|
||||
|
||||
### 收报与处理
|
||||
|
||||
1. `InboxPoller` 默认每秒扫描 `DATE_PROCESSED IS NULL` 的信箱记录,在自有 PG 中建立 `PROC_STATE(PENDING)`。重复扫描不能重复入队;入队失败留待重扫。目标形态的扫描谓词与水位的唯一口径见 message-lifecycle.md §5.1。
|
||||
1. `InboxPoller` 默认每秒按 ID 区间扫描水位 `W` 之后的信箱记录(`ID > W`,**不以处理标记为谓词**),在自有 PG 中建立 `PROC_STATE(PENDING)`;水位与入队在同一事务推进。重复扫描不能重复入队,中断后由重扫补建。扫描谓词与水位的唯一口径见 message-lifecycle.md §5.1。
|
||||
2. 主泵只处理最小未完成 `MSG_ID`。解析报文、绑定业务身份并去重后,分派给 SCHD/FLOP/FDEL/ADFT 处理器。
|
||||
3. 在自有 PG 同一事务内(先取 `PIPELINE_LOCK`)保存航班状态变更(`FLIGHT_SCHD` 与明细表)、处理结果、`MSG_EVENT` 待发事件与回填待办预登记。
|
||||
4. 事务提交后,回填共享信箱的处理标记(外部副作用,补偿保障)。
|
||||
3. 在自有 PG 同一事务内(先取 `PIPELINE_LOCK`)保存航班状态变更(`FLIGHT_SCHD` 与明细表)、`MSG_EVENT` 待发事件、处理终态与回填意图。
|
||||
4. 事务提交后,补写共享信箱的处理标记(外部副作用,由回填退避重试与超期强制补写保障)。
|
||||
|
||||
### 投递
|
||||
|
||||
@@ -87,7 +87,7 @@ CIIMS / AODB 等上游
|
||||
|
||||
| 存储 | 承载内容 | 职责说明 |
|
||||
|---|---|---|
|
||||
| 自有 PostgreSQL | 单行锁 `PIPELINE_LOCK`、处理状态 `PROC_STATE`、待发事件 `MSG_EVENT`、请求跟踪 `REQ_TRACK`、回填待办 `BACKFILL_TODO`、航班当前态 `FLIGHT_SCHD` + 9 张明细表、留痕 `SCHD_SNAP_LOG` | 本系统唯一业务数据库。消息处理、状态推进与待发事件在单事务内原子提交;本地事务只在此库。 |
|
||||
| 自有 PostgreSQL | 单行锁 `PIPELINE_LOCK`、处理状态与回填事实 `PROC_STATE`、消费水位 `INBOX_CURSOR`、待发事件 `MSG_EVENT`、请求跟踪 `REQ_TRACK`、航班当前态 `FLIGHT_SCHD` + 9 张明细表、留痕 `SCHD_SNAP_LOG` | 本系统唯一业务数据库。消息处理、状态推进、处理终态、回填意图与待发事件在单事务内原子提交;本地事务只在此库。 |
|
||||
| 共享 MySQL | `CMINMSGS` 入站信箱、`COUTMSGS` 出站信箱 | 外部系统所有。本系统仅执行约定的信箱读写与处理标记回填,不建表、不迁移 schema、不写历史表;由库方按 Q9 执行的清除与历史归档见 message-lifecycle.md §6。兼容 HTTP 入口可按既有契约写入入站信箱。 |
|
||||
|
||||
**不使用跨库事务。** PG 事务只能保证“处理结果与待发事件一起提交”,不能覆盖 MySQL 回填或 Kafka 发送等外部副作用。跨存储依靠幂等、重试和持久化补偿恢复;各中断位置的判定与恢复动作统一见 [message-lifecycle.md](message-lifecycle.md) §4,本文不重复。
|
||||
|
||||
+15
-13
@@ -17,12 +17,12 @@
|
||||
|
||||
| 记录 | 用途 | 关键约束 |
|
||||
|---|---|---|
|
||||
| `PROC_STATE` | 入站消息的处理状态、身份、重试次数和错误原因 | `MSG_ID = CMINMSGS_ID` 主键防止重复入队;`IDENTITY_KEY` 唯一约束防止业务重复;按最小未完成消息 ID 取队头。 |
|
||||
| `PROC_STATE` | 入站消息的处理状态、身份、重试次数、错误原因与回填事实 | `MSG_ID = CMINMSGS_ID` 主键防止重复入队;`IDENTITY_KEY` 唯一约束防止业务重复;按最小未完成消息 ID 取队头;`RECEIVED_AT` 与 `BACKFILL_*` 承载 §5.2 超期判据与回填重试。 |
|
||||
| `MSG_EVENT` | 等待投递的事件(outbox) | `EVENT_ID` 决定投递顺序;`TARGET` 区分 `KAFKA:msg` / `KAFKA:schd`;`PARTITION_KEY` 恒为 `FLID`;`EVENT_TYPE` 区分 UPSERT 与 TOMBSTONE。 |
|
||||
| `REQ_TRACK` | 上游请求及应答关联 | 保存请求类型、覆盖运营日、发送方、出站信箱 ID 与发送/完成时间;同类只允许一个开放请求。登记、超时与应答匹配尚未实现(§10)。 |
|
||||
| `REF_MASTER` | 静态参考数据(目标表) | `(RTYPE, RKEY)` 唯一;尚未建表,客户端与刷新流程见 user-stories.md US-13/US-14(US-14 两类映射的存储落点未定)。 |
|
||||
| `FLIGHT_SCHD` | 航班标量及单值异常字段 | `FLID` 主键;`OPERATION_DAY` 一经确定不可变;版本与最近消息 ID 用于追踪。变长资源集合存于 9 张明细表,规则见 [flight-state.md](flight-state.md) §2,不在此重复。 |
|
||||
| `BACKFILL_TODO` | 共享信箱回填重试 | 回填意图与业务终态同事务预登记,提交后回填成功即删除;待办再次落账失败的崩溃窗口仍在(§10)。 |
|
||||
| `INBOX_CURSOR` | 共享信箱消费水位 `W` | 单行游标;只随新 ID 成功入队推进,遇空洞即停,空洞超期判定为永久([message-lifecycle.md](message-lifecycle.md) §5.1)。 |
|
||||
| `PROC_STATE_HST` | 终态处理记录的归档目标 | 尚未建表;不得改写为共享库历史表。 |
|
||||
|
||||
字段与索引定义以 `src/main/resources/db/migration/V1__flight_state_baseline.sql` 为准;Oracle 11g 的迁移形态见 `src/main/resources/db/migration/oracle11g/`(占位,未接入任何 Flyway 配置)。报文原文仍从共享信箱读取,因此必须协调原文保留期,不能在消息尚需处理或重放时提前清理;生命周期、回填与清除契约的唯一定义见 [message-lifecycle.md](message-lifecycle.md)。
|
||||
@@ -69,9 +69,9 @@ SNDR | TYPE | STYP | SEQN
|
||||
|
||||
### 3.1 收报
|
||||
|
||||
`InboxPoller` 默认每秒按 ID 升序、有限批次(`claim-batch`,默认 50)读取 `DATE_PROCESSED IS NULL` 的信箱记录,经 `InboxEnqueue` 在自有 PG 建立 `PENDING`;重复扫描幂等,入队失败留待下一轮。收报层不解析业务载荷,也不回填已处理标记。
|
||||
`InboxPoller` 默认每秒按 ID 升序、有限批次(`claim-batch`,默认 50)读取水位之后的信箱记录(`ID > W`,**不以处理标记为谓词**),在自有 PG 建立 `PENDING` 并把水位推进到连续上界;入队与水位推进在同一 PG 事务内提交,重复扫描幂等、中断后重扫补建。收报层不解析业务载荷,也不回填已处理标记。
|
||||
|
||||
水位的定义(连续上界、遇空洞即停)、扫描谓词与补偿窗口唯一见 [message-lifecycle.md](message-lifecycle.md) §5.1;水位不是已处理标记,其有效性以 Q2 的 ID 单调承诺为前提。快路径水位与受控补扫仍是目标形态,当前按 `afterId=0` 每轮全量扫描(§10),迟提交与空洞场景的顺序保证在启用前必须验证。
|
||||
水位 W 与扫描谓词的完整口径(连续上界、遇空洞即停、空洞老化)唯一见 [message-lifecycle.md](message-lifecycle.md) §5.1;水位不是已处理标记,其有效性以 Q2 的 ID 单调承诺为前提。空洞老化阈值取 `pipeline.max-commit-delay`。
|
||||
|
||||
兼容 HTTP 入口执行“写入共享信箱 → PG 入队”。两步不在同一事务中:信箱成功而 PG 失败时,原文不能丢失,由轮询补建;客户端失败重试可能再次写信箱,业务身份去重仍然必需。
|
||||
|
||||
@@ -100,8 +100,8 @@ SNDR | TYPE | STYP | SEQN
|
||||
|
||||
- 原文缺失归为 `MALFORMED`;读取异常不能伪装成“缺失”,应进入基础设施重试。
|
||||
- 忽略规则(`LDM / REGN / RSTA / EROR` → `SKIPPED`)尚未实现(§10);不能因类型未覆盖就把合法忽略报文当非法报文处理。
|
||||
- 航班变更、待发事件、处理终态与回填待办在同一 PG 事务原子提交;跨存储双写窗口已根除。
|
||||
- 终态回填发生在提交后:成功、业务重复与协议拒绝包持有可用 META 并回填;缺 META 或解码失败的死信不在常规回填范围,按超期补写规则处理([message-lifecycle.md](message-lifecycle.md) §5.2;US-09/Q7)。`PENDING / FAILED` 禁止回填;影子环境禁写。
|
||||
- 航班变更、待发事件、处理终态与回填意图在同一 PG 事务原子提交(终态由处理器在自己的事务内落库,`MessageProcessor` 不再单独补写终态);跨存储双写窗口已根除。
|
||||
- 终态提交后立即尝试一次回填,失败由回填扫描按退避重试,抵达超期期限 R 时按 [message-lifecycle.md](message-lifecycle.md) §5.2 强制补写(US-09/Q7)。回填只需消息 ID,因此缺 META 或解码失败的死信同样可补写。`PENDING / FAILED` 禁止回填;影子环境禁写。
|
||||
|
||||
## 4. 日计划快照与请求匹配
|
||||
|
||||
@@ -163,7 +163,7 @@ REGISTERED → SENT → WAITING → DONE
|
||||
|
||||
`ReplayService` 只允许 `CODEC_ERROR / UNSUPPORTED / INFRA / EXHAUSTED` 从 `FAILED / DEAD` 回到 `PENDING`,重置次数与下次执行时间,保留身份与错误审计。它按错误类整批重放,尚无按记录预检、操作审计与管理入口(US-10)。旧消息进入终态后后续消息可能已执行,**重新入队不等于恢复历史顺序**;人工重放前必须评估状态覆盖和版本保护,不能直接批量重放到生产。
|
||||
|
||||
**维护作业**:`JobRunner` 用独立 daemon 线程每 30 秒触发 `BackfillSweepJob`(回填补偿扫描,指数退避 30 秒起步、封顶 15 分钟),每天机场时区 03:30 后触发一次 `HistorySweepJob`(§6.2)。作业不再经 `PUMP_JOB` 队列插队,不参与消息 FIFO,也不使到期消息饥饿。作业与回填通道的生命周期口径见 [message-lifecycle.md](message-lifecycle.md) §3/§4。
|
||||
**维护作业**:`JobRunner` 用独立 daemon 线程每 30 秒触发 `BackfillService.sweep`(回填补写扫描,指数退避 30 秒起步、封顶 15 分钟),每天机场时区 03:30 后触发一次 `HistorySweepJob`(§6.2)。回填意图在终态事务内登记在 `PROC_STATE`(`BACKFILL_NEXT_AT/ATTEMPTS/ERROR`),不再有独立待办表。作业不再经 `PUMP_JOB` 队列插队,不参与消息 FIFO,也不使到期消息饥饿。作业与回填通道的生命周期口径见 [message-lifecycle.md](message-lifecycle.md) §3/§4。
|
||||
|
||||
### 6.2 历史清理与归档
|
||||
|
||||
@@ -183,6 +183,8 @@ REGISTERED → SENT → WAITING → DONE
|
||||
|
||||
- `pipeline.autostart` 与 `msgx.stubs`:分别控制管道启动与内存适配器;生产禁止 stub,默认不自动启动。
|
||||
- `pipeline.poll-interval / claim-batch / max-attempts / backoff / head-deadline`:控制轮询节奏、批次、重试上限、退避与队头滞留;这些参数不能改变 FIFO。
|
||||
- `pipeline.max-commit-delay / overdue-backfill / backfill-batch`:空洞老化阈值(Q2 最大提交时延)、超期补写期限 R(Q6)与回填扫描批量;R 与老化阈值都不能为提速而下调。
|
||||
- `mailbox.processed-value`:写回共享信箱的处理标记值,仅限库方认可的 legacy 值集(Q7)。
|
||||
- `schd.flush-period / flush-limit`:控制状态通知的聚合延迟与批量大小。
|
||||
- `identity.include-day-boundary`:影响去重语义,不能作为普通调优项切换。
|
||||
- `mailbox.shared-mysql.enabled` 与 `history.history-store-enabled`:分别门控真实信箱与历史存储接线,默认关闭。
|
||||
@@ -195,12 +197,12 @@ REGISTERED → SENT → WAITING → DONE
|
||||
|
||||
| 场景 | 必须验证的结果 |
|
||||
|---|---|
|
||||
| 重复扫描、入队中断、较小 ID 迟到 | 不重复入队、不丢记录、不让后续消息越序。 |
|
||||
| 重复扫描、入队中断、较小 ID 迟到 | 不重复入队、不丢记录、不让后续消息越序;终态而未回填的行不得阻断后续消息发现。 |
|
||||
| 队头失败、退避及作业竞争 | 消息不越队;到期后恢复;作业不使消息无限饥饿。 |
|
||||
| 同身份多条记录、失败后重试、归档后重复 | 只产生一次有效业务处理,不把自身重试判为重复。 |
|
||||
| PG 事务失败、快照重复或迟到 | 整体回滚重试、不重复推进版本、不回退状态、不误删增量航班。 |
|
||||
| 整包协议拒绝(运营日冲突、声明数不符) | 整包不落地、整体回滚,既有状态与版本不变,消息终态为 `DEAD(PROTOCOL)`。 |
|
||||
| PG 提交失败、信箱回填失败 | 事件与处理结果一起回滚;已提交结果只由回填待办补偿,不重放业务。 |
|
||||
| PG 提交失败、信箱回填失败 | 事件、处理终态与回填意图一起回滚;已提交结果只补写标记,不重放业务;中间态永不补写。 |
|
||||
| 投递确认丢失、批次失败、次数耗尽 | 允许可识别的重发、保持目标顺序、整批退避并保留死信。 |
|
||||
| 请求超时、无匹配 RESP、时间单位不一致 | 不误用迟到应答,不提前完成请求。 |
|
||||
| stub 误配置、重复实例、停机中断 | 生产拒绝不安全启动,工作线程能正确退出。 |
|
||||
@@ -213,11 +215,11 @@ REGISTERED → SENT → WAITING → DONE
|
||||
|
||||
| 关注点 | 主要入口 |
|
||||
|---|---|
|
||||
| 收报与兼容接口 | `ingress/InboxPoller.kt`、`InboxEnqueue.kt`、`InboxService.kt`、`InboxController.kt` |
|
||||
| 收报与兼容接口 | `ingress/InboxPoller.kt`、`InboxService.kt`、`InboxController.kt` |
|
||||
| 解码 | `codec/JacksonXmlCodec.kt`、`SisWireMapper.kt`、`SisMessageBody.kt` |
|
||||
| 调度与处理 | `processing/Pump.kt`(含 `MessageProcessor`)、`DynamicProcessors.kt`(FLOP/FDEL/ADFT)、`Identity.kt` |
|
||||
| 日计划 | `processing/ScheduleProcessor.kt`;请求协调尚无实现(`REQ_TRACK` 见 `infra/persistence/`) |
|
||||
| 投递与作业 | `delivery/Dispatcher.kt`、`jobs/JobRunner.kt`、`BackfillSweepJob.kt`、`HistorySweepJob.kt` |
|
||||
| 回填与投递作业 | `processing/BackfillService.kt`、`jobs/JobRunner.kt`、`HistorySweepJob.kt`、`delivery/Dispatcher.kt` |
|
||||
| 持久化与恢复 | `infra/persistence/`、`infra/retry/`(`ProcFailure` / `ReplayService` / `FailureScheduler`) |
|
||||
| 启停与配置 | `PipelineLifecycle.kt`、`config/PipelineProps.kt`、`config/HistoryProps.kt` |
|
||||
|
||||
@@ -225,8 +227,8 @@ REGISTERED → SENT → WAITING → DONE
|
||||
|
||||
以下缺口直接影响上述设计是否成立,不能以类或接口已存在作为完成依据:
|
||||
|
||||
- **事务与外部副作用**:状态、事件、终态与回填待办同 PG 事务已实现,回填失败落 `BACKFILL_TODO` 并由 `BackfillSweepJob` 到期重试。剩余缺口在“提交后回填前崩溃”与“待办落账再次失败”两个窗口,不能认定补偿最终必达;两个窗口的恢复判定见 [message-lifecycle.md](message-lifecycle.md) §4。
|
||||
- **收报与调度**:轮询仍从 `afterId=0` 每轮全量扫描,持久水位与受控补扫未完成;较小 ID 迟提交与空洞场景的顺序保证未验证。滞留判据使用 `updatedAt` 与直取系统时间,未基于稳定起始时刻。
|
||||
- **事务与外部副作用**:状态、事件、处理终态与回填意图同 PG 事务提交;回填意图落在 `PROC_STATE`(`BACKFILL_AT/NEXT_AT/ATTEMPTS/ERROR`),`BACKFILL_TODO` 已随 V2 迁移下线,[message-lifecycle.md](message-lifecycle.md) §4 的两个崩溃窗口(提交后回填前崩溃、待办二次落账失败)不再存在。回填本身仍是跨库单写,失败按退避重试并由超期期限 R 兜底;R 的取值待 Q6 确认。
|
||||
- **收报与调度**:水位 W 落库并与入队同事务推进,遇空洞即停、空洞超过 `max-commit-delay` 判定为永久(Q2 未书面确认前该阈值是假定值);较小 ID 迟提交与空洞场景的顺序保证仍待与库方联合验证。滞留判据仍使用 `updatedAt` 与系统时钟(已改经可注入 `Clock`),未基于稳定起始时刻(Q6)。
|
||||
- **快照与业务能力**:DNLD/RESP/ADFT 与 FLOP/FDEL 处理器、整包校验与跨运营日整包拒绝均已接入;但 RESP 应答守卫与出站请求未实现,忽略规则(US-04)未实现,29 类 FLOP 与参考应答的逐类矩阵未补全,ADFT 缺失字段与 `FLID` 重用语义待上游确认。
|
||||
- **航班读写**:唯一写入口与权威读已落地;ROUT/ERUT 联合主键、空值/未知属性保真、事件在事务内只算一次、逐航班多次查询仍待修正(见 flight-state.md §6)。`/all/flights` 尚未实现。
|
||||
- **请求、参考数据与归档**:`REQ_TRACK` 表与仓储已建,但无运行时协调与 `COUTMSGS` 出站适配;`REF_MASTER` 未建表;`PROC_STATE_HST` 未建表。航班历史清理脚手架已实现,历史存储未接通时删 0 条。
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
| `FLIGHT_SCHD` | 一行一个 `FLID`,保存标量字段、`STATE`、`STATE_VERSION`、`OPERATION_DAY`、最近消息 ID 和审计时间。 |
|
||||
| 8 张资源明细表 | 登机门、值机柜台、转盘、计划机位、滑槽、延误、靠撤桥、轮挡等变长集合;主键为 `(FLID, ORDINAL)`。 |
|
||||
| `FLIGHT_ROUTE_POINT` | ROUT 与 ERUT 两类路线点,使用 `ROUTE_KIND` 区分;主键应包含该列,避免两类路线的序号冲突。 |
|
||||
| `PROC_STATE` | 信箱消息的处理终态和业务身份幂等记录。 |
|
||||
| `PROC_STATE` | 信箱消息的处理终态、业务身份幂等记录,以及回填事实(`RECEIVED_AT` / `BACKFILL_*`)。 |
|
||||
| `MSG_EVENT` | 事务 outbox,承载整态投影、变更通知和删除 tombstone。 |
|
||||
| `BACKFILL_TODO` | 共享信箱回填的可重试待办。 |
|
||||
| `INBOX_CURSOR` | 共享信箱消费水位(读取进度,与处理标记互不替代)。 |
|
||||
| `SCHD_SNAP_LOG` | 日计划处理留痕,只追加、可重建,不参与状态决策。 |
|
||||
|
||||
### 2.1 航班身份与运营日
|
||||
|
||||
@@ -43,7 +43,7 @@ MSG_EVENT PENDING ──→ SENT
|
||||
|
||||
- `PENDING` 与 `FAILED` 是处理中的状态,`FAILED` 继续退避重试并占用 FIFO 队头;`SUCCEEDED / SKIPPED / DEAD` 是终态,到达后队列方可推进。
|
||||
- `DEAD` 与 `FAILED` 不是不可逆:经人工批准,指定错误类别的记录可重新置回 `PENDING` 处理(放行范围见 §9 / design.md §6.1)。重放处理的是当前状态,不恢复历史处理顺序。
|
||||
- 航班变更、待发事件、处理终态与回填待办预登记四类写入位于同一个 PG 事务,一起提交或一起回滚;信箱处理标记写在该事务之后,依赖 `BACKFILL_TODO` 补偿记录最终写入。
|
||||
- 航班变更、待发事件、处理终态与回填意图四类写入位于同一个 PG 事务,一起提交或一起回滚;信箱处理标记写在该事务之后,由与终态同行的回填意图驱动重试与超期补写。
|
||||
|
||||
## 3. 处理阶段
|
||||
|
||||
@@ -53,9 +53,9 @@ MSG_EVENT PENDING ──→ SENT
|
||||
| 取队头 | 最小未完成 `MSG_ID` | 本批处理消息 | 表状态即队列 | 无队头则等待 |
|
||||
| 解码与身份 | 信箱原文 | `DecodedMessage`、`IDENTITY_KEY` | 身份唯一约束 | 报文非法→`DEAD`;处理能力不足→`FAILED` 退避 |
|
||||
| 事务处理 | 当前完整状态 + 报文载荷 | 航班变更、终态、事件、回填待办 | 消息 ID + 业务身份 | 事务整体回滚重试 |
|
||||
| 提交后回填 | 终态消息 | 信箱处理标记 | 标记单调(§11) | 记入 `BACKFILL_TODO` 重试 |
|
||||
| 提交后回填 | 终态消息 | 信箱处理标记 | 标记单调(§11) | 终态事务内登记回填意图;失败退避重试,超期强制补写 |
|
||||
| 投递 | `MSG_EVENT` | 下游确认 | `EVENT_ID` | 退避至 `DEAD`;至少一次 |
|
||||
| 补偿作业 | 回填待办 / 到期批次 | 追平标记 | 待办主键 | 指数退避,独立线程执行 |
|
||||
| 补偿作业 | 到期或已达 `NOW − R` 的终态记录 | 追平标记 | 消息 ID(仅空标生效) | 指数退避,独立线程执行;`R` 覆盖退避 |
|
||||
|
||||
处理器只在持有 `PIPELINE_LOCK` 的事务内写入自有 PG 状态,不操作信箱与 Kafka;全部外部副作用发生在事务提交之后。
|
||||
|
||||
@@ -67,17 +67,17 @@ MSG_EVENT PENDING ──→ SENT
|
||||
|---|---|---|
|
||||
| 已落信、未入队 | 信箱无标记且 PG 无记录 | 重扫补建入队记录 |
|
||||
| 事务执行中 | PG 无该消息终态 | 事务整体回滚,按 `PENDING` 重新处理 |
|
||||
| 事务已提交、标记未写 | `BACKFILL_TODO` 存在待办 | 仅补写标记;业务处理结果保持不变 |
|
||||
| 事务已提交、标记未写 | 终态行仍持有回填意图(`BACKFILL_NEXT_AT` 非空) | 仅补写标记;业务处理结果保持不变 |
|
||||
| 回填待办登记失败(与业务同事务) | 事务未提交 | 同"事务执行中",不构成独立窗口 |
|
||||
| 标记写入中途 | 标记仍为空 | 重新写入;重复写入同一值无副作用 |
|
||||
| 下游已接收、`SENT` 未置 | 事件仍 `PENDING` | 允许重发,下游按事件身份去重 |
|
||||
|
||||
两个缺口在闭环前不能宣称恢复完整(口径与 design.md §10「事务与外部副作用」一致):
|
||||
两个窗口已随"终态与回填意图同体同行"消除(口径与 design.md §10「事务与外部副作用」一致),不再是缺口:
|
||||
|
||||
1. 事务提交后、回填动作开始前的崩溃窗口(提交后回填前崩溃);
|
||||
2. 回填待办二次落账失败(待办落账再次失败)。
|
||||
1. **提交后回填前崩溃**:回填意图与处理终态是同一条记录的同一次写入、同一个事务;重启后扫描按该意图继续补写。
|
||||
2. **回填待办二次落账失败**:不存在第二处落账——意图就在终态行上。
|
||||
|
||||
两项登记于 design.md §10 与 user-stories.md US-09;本表的恢复判定以这两个窗口为前提。
|
||||
仍属未闭环的是回填本身在跨库单写期间的持续失败:由退避重试与 §5.2 的超期期限 `R` 兜底(登记于 design.md §10 与 user-stories.md US-09)。
|
||||
|
||||
## 5. 消费水位、超期补写与历史积压
|
||||
|
||||
@@ -92,6 +92,8 @@ MSG_EVENT PENDING ──→ SENT
|
||||
|
||||
日常执行方式:快路径从 `ID > W` 起按升序有限批次扫描;另按周期对窗口内可能迟到或空洞的行做补偿扫描。需要区分:水位表示"读取进度",与"已处理标记"是两个事实,不能互相替代。
|
||||
|
||||
**空洞老化**:自增回滚等会在 ID 序列中留下永久空位,而 Q2 只承诺 ID 单调、不承诺无空洞。W+1 处的空洞持续超过最大提交时延仍未被补齐时,即判定为永久空洞并放行水位(实现取 `pipeline.max-commit-delay`);放行只跳过空洞本身,不越过任何已存在的行。没有这条规则,水位会永久停摆于第一个空位,其后的行再也不会入队。
|
||||
|
||||
两条扫描路径都按 **ID 区间**取行,不以处理标记为扫描谓词;标记只用于回填与库方清除,不参与消息发现。否则已入队但尚未回填的行会永久占据批次,这正是 US-01 条目 3 与 Q2 要求排除的场景。
|
||||
|
||||
### 5.2 超期标记补写
|
||||
@@ -104,6 +106,7 @@ MSG_EVENT PENDING ──→ SENT
|
||||
- 中间态(`PENDING` / `FAILED`)不适用本规则:处理未完成时不打标,也不允许被清除。
|
||||
- 补写值仅限于库方认可的 legacy 值集(Q7);死信、业务重复等内部原因记录在 `PROC_STATE` 与审计日志,不在信箱新增枚举。
|
||||
- 补写只针对空标记;已有值不回撤、不覆盖,重复执行无副作用。
|
||||
- 判据不依赖独立待办表:回填意图与处理终态同行(§2/§4),扫描条件为「终态 + 未确认标记 + (已到期 或 接收时间早于 `NOW − R`)」——`R` 是覆盖退避的硬期限,保证该条件在有限时间内必然被处理。
|
||||
|
||||
该规则同时保证三件事成立:收报表不被永不回填的记录占满(Q2 中"积压挡批"场景)、§6 的清除边界可以达到、重放期限与信箱保留期具备可核验的下限关系(§9)。
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
4. PG 不可用或批次中途失败时不改信箱标记;恢复后补建遗漏,记录失败次数与扫描进度。
|
||||
5. 较小 ID 迟提交、ID 有空洞、兼容入口先入队较大 ID 时,必须遵守经 Q2 确认的发现与顺序协议;不能用“最终会重扫”冒充严格 FIFO。
|
||||
|
||||
**当前基础与落点**:`ingress/InboxPoller.kt`、`InboxEnqueue.kt`、`infra/persistence/jdbc/JdbcCminmsgInboxRepository.kt` 已有轮询与判重;固定 `afterId=0` 全量扫描,水位与补扫策略未实现(每轮顺带对账回填待办,见 US-09)。扩展 `InboxPollerTest`,补真实 PG/MySQL 中断恢复测试。
|
||||
**当前基础与落点**:`ingress/InboxPoller.kt` 按 ID 区间扫描(`ID > W`,不以处理标记为谓词),水位落 `INBOX_CURSOR` 并与入队同事务推进;`JdbcCminmsgInboxRepository.readRange/maxId` 与 `ProcStateRepository.insertIfAbsent` 承担发现与幂等入队。空洞老化阈值取 `pipeline.max-commit-delay`。剩余:Q2 未书面确认前,老化阈值与严格顺序仍是假定口径;真实 MySQL 的中断恢复与迟提交联合测试待现场环境。
|
||||
|
||||
**前置**:共享库读契约;Q2 决定严格顺序的端到端验收。水位与扫描谓词口径以 [message-lifecycle.md](message-lifecycle.md) §5.1 为准。
|
||||
|
||||
@@ -190,7 +190,7 @@
|
||||
4. 重复补偿效果幂等,保留稳定的完成时间与审计;重放后的新处理结果不能被旧回填任务覆盖。非法报文缺 META 时也有明确回填方式。
|
||||
5. 影子模式禁写,双跑仅一个系统持有标记写权;暴露 PG 终态、回填状态、积压、最老年龄与持续失败告警。
|
||||
|
||||
**当前基础与落点**:回填意图已在业务事务内预登记到自有 `BACKFILL_TODO`;提交后同步回填(写 `DATE_PROCESSED` 与 `PROCESSED`),失败由 `BackfillSweepJob` 到期重试(每 30 秒、指数退避)。剩余缺口:缺 META 或解码失败的死信回填方式,以及“待办落账后再失败 / 提交后崩溃”两个窗口的补偿闭环。
|
||||
**当前基础与落点**:回填意图与处理终态同体同行(`PROC_STATE.BACKFILL_*`),随业务事务提交,`BACKFILL_TODO` 已随 V2 迁移下线;提交后立即尝试一次,失败由 `BackfillService.sweep` 到期重试(每 30 秒、指数退避 30 秒起步封顶 15 分钟),接收时间超过超期期限 `R` 时强制补写(§5.2)。死信同样可补写——回填只需消息 ID,不依赖 META。剩余:Q7 的标记值集与写权限书面确认;影子环境禁写尚未实装。
|
||||
|
||||
**前置**:US-03 终态接口;Q7、共享库更新权限。覆盖四类终态、事务回滚、重复补偿和重放竞争;生命周期、超期补写与清除口径以 [message-lifecycle.md](message-lifecycle.md) §4/§5.2/§6 为准。
|
||||
|
||||
@@ -305,7 +305,7 @@
|
||||
| 编号 | 问题与当前口径 | 解除阻塞的产物 |
|
||||
|---|---|---|
|
||||
| Q1 权威存储(方向已定) | 当前 PG 单库权威,主表 + 无损明细;现场供库目标为 Oracle 11g。 | 单库决策已采纳,Oracle 完整适配与部署验收仍待交付;不得退回 Redis 双写。 |
|
||||
| Q2 入队顺序 | 水位+补扫无法自动保证较小 ID 迟提交不越序;当前有限批扫描也可能被未回填记录挡住。 | 库方 ID/提交顺序约束,或明确的发现完整性与暂停/恢复协议;晚提交、空洞、兼容入口与重扫联合测试。不能凭空假定 ID 连续。 |
|
||||
| Q2 入队顺序 | 空洞老化阈值取自"最大提交时延",但库方尚未书面承诺 ID 单调与提交时延,因此迟提交不越序仍无端到端保证。 | 库方 ID/提交顺序约束,或明确的发现完整性与暂停/恢复协议;晚提交、空洞、兼容入口与重扫联合测试。不能凭空假定 ID 连续。 |
|
||||
| Q3 HTTP 契约 | 目标 ResponseDto 与现有 text/plain ID 不同;请求媒体类型目标已列出,错误码、状态码、查询格式等仍需对拍。 | 每个保留接口的真实请求/响应样例、错误表和契约测试;10MB 的字节口径、字符集及兼容变更说明一起固定。 |
|
||||
| Q4 Kafka wire | 当前 msg/schd 均按 `FLID` 逐航班发送(schd 由 `flushSchd` 合并为每个 FLID 的最新状态),与 legacy“多 FLID 数组”形态不同;msg 是否需按 `SNDR` 分区尚未接线。 | 下游确认发送粒度、key、去重标识放置、分区内顺序及批次确认策略;未定案前不改动现役消费契约。 |
|
||||
| Q5 请求匹配 | 目标优先 SEQN 回显,但回显是否可靠需确认;DTTM 降级存在跨代误匹配,尤其旧应答到达新请求期间。 | 15 类请求/响应样例、回显字段与时间格式;降级风险是否接受及拒绝条件。默认超时仍为 RQFD 60 秒、RQRD 30 秒。 |
|
||||
|
||||
Reference in New Issue
Block a user