refactor(ingress): 移除 G1 窗口补偿扫描与迟到到达检测

实际负载不足 10 条/秒,G1 属过度防御。删除迟到到达检测机制、
existingIds 接口、PipelineCounters 字段、lateDetect 配置,
以及文档中 CLM-1/CLM-2 声明与 G1 缺口索引。
This commit is contained in:
windyboy
2026-09-13 08:08:43 +08:00
parent a6b2120836
commit b8738554e6
20 changed files with 10 additions and 322 deletions
+2 -2
View File
@@ -50,7 +50,7 @@ CIIMS / AODB 等上游
| 模块 | 职责与边界 |
|---|---|
| `ingress` | 轮询信箱、持久化入队、补偿重扫及兼容 HTTP 写入;不解析业务报文。 |
| `ingress` | 轮询信箱、持久化入队及兼容 HTTP 写入;不解析业务报文。 |
| `codec` | XML 解码,区分非法报文与可修复的解码失败。 |
| `processing` | FIFO 调度、业务身份绑定与去重、领域决策与落库(SCHD/FLOP/FDEL/ADFT):纯领域逻辑只返回决策;Processor 作为事务协调器,在锁事务内完成状态写入、事件与回填意图登记,不直接触碰 Kafka。 |
| `delivery` | 消费待发事件,负责按目标保序、`schd` 聚合、投递和失败重试。 |
@@ -68,7 +68,7 @@ CIIMS / AODB 等上游
本节只列约束的**归属**;完整定义与验证映射见 [invariants.md](invariants.md),实现与演进不得违反:
- 消息严格 FIFO`INV-3``INV-4``INV-5`(发现完整性依赖 `PRE-2`/`PRE-3`,当前不可对外声明,见 CLM-1/CLM-2)。
- 消息严格 FIFO`INV-3``INV-4``INV-5`(发现完整性依赖 `PRE-2`/`PRE-3`,当前不可对外声明)。
- 动态状态单写者与写者集合互斥:`D2``INV-18`
- 身份去重:`INV-9`(身份组成见 design.md「消息、身份与决策」)。
- 快照可恢复与运营日不可变:`INV-12``INV-13`
+2 -2
View File
@@ -25,7 +25,7 @@
### A.1 ID 与可见性
- **C-1** ID 单调:信箱 ID 按提交顺序分配,已发布水位之下不再出现更小的新 ID。`[待确认 Q2]`
- **C-2** ID 分配 → 事务可见时延上界由库方**直接给出**。该值决定空洞老化阈值与补偿扫描窗口宽度**不可由 SIS 报文 `Expiry` 推导**`Expiry` 是报文保留与传输恢复口径,与「ID 分配后多久对读事务可见」不是同一个量)。`[待确认 Q2]`
- **C-2** ID 分配 → 事务可见时延上界由库方**直接给出**。该值决定空洞老化阈值;**不可由 SIS 报文 `Expiry` 推导**`Expiry` 是报文保留与传输恢复口径,与「ID 分配后多久对读事务可见」不是同一个量)。`[待确认 Q2]`
- **C-3** ID 空间不复位、不复用、不回退:含表轮换、备份恢复、`AUTO_INCREMENT` 归零。采用整表轮换方案时,新表种子必须 ≥ `max(ID)+1`,保证 ID 不断链;本系统的水位 `W` 是不可逆单游标,ID 回退会导致其后所有行永久不可见。`[待确认 Q2]`
- **C-4** 报文行不可变:同一业务身份(`SNDR|TYPE|STYP|SEQN`)的重发必为同一内容。若上游会以同一身份改发正文,需要另定识别规则(`Q15`)。`[待确认 Q15]`
@@ -72,7 +72,7 @@
| 编号 | 事项 | 当前假定 | 阻塞 | 状态 |
|---|---|---|---|---|
| Q1 | 权威存储(内部方向) | 自有 PG 单库权威 + 无损明细;现场供库目标 Oracle 11g | — | 已定案(内部),Oracle 适配与部署验收另计 |
| Q2 | 信箱 ID 单调、ID 分配→事务可见时延上界、ID 空间不复位;空洞与迟到处置 | 时延按 5 分钟 `max-commit-delay`(**缺少依据的占位值**,不可由 SIS `Expiry` 推导) | 发现完整性声明、空洞老化阈值、补偿扫描窗口、水位不可逆性 | 未确认 |
| Q2 | 信箱 ID 单调、ID 分配→事务可见时延上界、ID 空间不复位;空洞与迟到处置 | 时延按 5 分钟 `max-commit-delay`(**缺少依据的占位值**,不可由 SIS `Expiry` 推导) | 发现完整性声明、空洞老化阈值、水位不可逆性 | 未确认 |
| Q3 | HTTP 契约:媒体类型、字符集、错误码、查询接口对拍 | 目标与上限见 `C-28` | 兼容入口验收 | 未确认 |
| Q4 | Kafka wire:发送粒度、key、去重标识、分区与批次确认 | 逐 `FLID` 发送,key=`FLID` | 投递契约 | 未确认 |
| Q5 | 请求匹配:回显字段可靠性与降级匹配 | `RQFD` 60 秒 / `RQRD` 30 秒超时 | 请求跟踪闭环 | 未确认 |
-4
View File
@@ -97,10 +97,6 @@
| 路径 | 目的 | 谓词 | 状态 |
|---|---|---|---|
| 快路径(日常) | 发现水位之后的新消息 | `ID > W ORDER BY ID ASC LIMIT claim-batch` | 已实现 |
| 只读迟到检测 | 复查被放行的空洞 ID 是否后来真的出现 | 进程内监视队列 + 批量存在性检查 | **临时观测**:只计数与告警,不补入队;监视队列有界、重启丢失,且只覆盖「曾被放行过的空洞 ID」。其存在理由是「把静默丢失变成可观测事实」,**退出条件**是 G1 补偿扫描交付或 `Q2` 承诺成立,届时删除该机制,不保留为长期能力。 |
| 补偿扫描 `[G1]` | 发现「提交晚于水位推进」的迟到行并安全处置 | 按周期重扫 `W` 之前一个窗口(宽度由 `C-2` 决定)内的 ID 区间 | 未实现 |
在补偿扫描交付前,「较小 ID 迟提交」没有补入队机制:快路径只读 `ID > W`,水位一旦越过某个 ID,该 ID 之后到达的消息永远不会被发现。**补偿扫描解决的是「不丢」,不是「不越序」**——补入队时更大的 ID 可能已经处理完,顺序已经越了;「不越序」只能由 `PRE-2` 承诺支撑(见 CLM-1 / CLM-2)。
### 4.3 切流播种
+2 -5
View File
@@ -53,8 +53,6 @@
| 编号 | 主张 | 依赖 | 当前可否声明 | 挂起原因 |
|---|---|---|---|---|
| CLM-1 | 严格 FIFO:迟到的小 ID 不会越序 | PRE-2、PRE-3、`Q2` | **不可** | 发现完整性依赖库方承诺;窗口补偿扫描(G1)未交付 |
| CLM-2 | 迟到报文不丢(可被发现并处置) | `G1` | **不可** | G1 未交付;现有阶段 0 只读检测仅计数告警,不补入队 |
| CLM-3 | 重放不产生重复业务副作用 | INV-20、`G-FLOP-IDEMPOTENT` | **不可** | 29 类 FLOP 幂等矩阵未补全;重放不恢复历史顺序 |
| CLM-4 | 回填不会被短暂故障放弃:最终打标,或进入可对账的放弃清单 | INV-8、`C-5``C-8` | **可声明(有条件)** | 条件:`R` 之前不放弃;`MISSING_ROW` 立即放弃并告警;放弃行须经人工对账才可用于清除判定(`C-8`)。原文保留另见 CLM-5 |
| CLM-5 | 重放窗口内原文仍可读 | `C-6``C-7``Q7``Q9` | **不可** | 清除语义与保留期未确认;「打标即清除」下无补救 |
@@ -74,7 +72,7 @@
| INV-2 | 重复扫描、入队中断 | 不重复入队、不丢记录;`InboxPollerTest` |
| INV-2 | 空洞老化与重置 | 阈值内不推进、不越过入队;超期只放行空洞本身;旧空洞补齐后新空洞获得完整窗口 |
| INV-2 | 水位写入与入队同事务 | 缺口(需真实 PG 事务用例,关联 ACM2-39 |
| INV-3 / CLM-1 | 较小 ID 迟 | **缺口基线已固定**`InboxPollerTest` 钉住「水位越过后到达的较小 ID 不被发现」;补偿扫描交付前禁止任何「迟到不越序」的验收声明 |
| INV-3 | 较小 ID 迟提交 | **缺口基线已固定**`InboxPollerTest` 钉住「水位越过后到达的较小 ID 不被发现」;水位遇空洞即停、空洞老化放行只跳过空洞本身 |
| INV-4 | 兼容入口与空洞并发 | 兼容入口登记的行超出水位、主泵不领取;`PipelineSmokeTest`「compat injected high id is not claimed until the watermark catches up」 |
| INV-3 | 队头失败、退避及作业竞争 | 消息不越队;到期后恢复;作业不使消息无限饥饿 |
| INV-5 | 终态未回填不阻断发现 | 缺口(补齐后应断言发现谓词不引用处理状态) |
@@ -105,7 +103,6 @@
| 缺口 | 含义 | 影响 |
|---|---|---|
| `G1` | 窗口补偿扫描未实现(Plane ACM2-41):水位越过后的迟到小 ID 没有补入队机制 | CLM-1、CLM-2 |
| `G-IGNORE` | 忽略规则(`LDM`/`REGN`/`RSTA`/`EROR`)未实现 | US-04;合法忽略报文当前按 `UNSUPPORTED` 处理 |
| `G-RESP-GUARD` | `RESP` 应答守卫未实现,当前与 `DNLD` 无差别进入快照写入 | 请求匹配闭环;`C-23` |
| `G-REQ-TRACK` | `REQ_TRACK` 无运行时协调器:出站适配、请求编码、超时与应答匹配未实现 | US-08;`C-24` |
@@ -120,4 +117,4 @@
| `G-COMPAT-HTTP` | compat 入口仍未实现 Q3 定案后的 ResponseDto、媒体类型、字符集、失败响应与请求体上限 | `C-28`US-02 |
| `G-REQ-OPEN-UNIQUE` | `REQ_TRACK` 尚无约束开放态 `(REQ_TYPE, OPERATION_DAY, SENDER)` 唯一性的部分索引 | US-08`G-REQ-TRACK` |
`G1` 沿用 Plane 既有编号(ACM2-41);其余为文档内稳定标记与 Plane 工作项的对应关系在 Plane 侧维护。
缺口标记与 Plane 工作项的对应关系在 Plane 侧维护。
-4
View File
@@ -25,8 +25,6 @@
| `msgx.pipeline.backfill-backoff-ms` | 目标参数(未实现) | ms / 档 | 假定 | 回填独立退避表;**当前不存在**,`BackfillService` 内硬编码 30 秒起步、封顶 15 分钟 `[G-BACKFILL-BACKOFF]` |
| `msgx.pipeline.backfill-backoff-cap-ms` | 目标参数(未实现) | ms | 假定 | 回填退避封顶;对应实现是代码内常量,尚无配置键 `[G-BACKFILL-BACKOFF]` |
| `msgx.pipeline.cutover-watermark` | 不设置 | `min\|zero\|max\|<id>` | 一次性运维决策 | 显式播种水位;非法值由启动自检挡下;升级实例拒绝重新播种 |
| `msgx.pipeline.late-detect-period` | `60s` | Duration | 假定 | 只读迟到检测周期;`≤0` 关闭;机制为临时观测(见 design 扫描路径) |
| `msgx.pipeline.late-detect-batch` | `200` | 条 | 假定 | 每轮复查的空洞 ID 上限 |
| `msgx.pipeline.delivery-batch` | `200` | 条 | 假定 | `KAFKA:msg` 每轮每目标领取上限 |
| `msgx.pipeline.delivery-drain-rounds` | `10` | 轮 | 假定 | 连取批数上限,让出循环跑 `schd` flush,防状态通知被积压饿死 |
| `msgx.pipeline.autostart` | `false` | 布尔 | 安全默认 | 启动即拉起收报 / 主泵 / 投递循环;需真实仓储或 `msgx.stubs=true` |
@@ -80,8 +78,6 @@
| `msgx.pipeline.backfill.abandoned` | 已放弃自动回填的条数 | **非 0 需人工对账** |
| `msgx.pipeline.backfill.oldest_unmarked_seconds` | 最老待回填年龄 | 决定实际回填延迟 |
| `msgx.pipeline.watermark.lag` | 水位落后信箱最新 ID 的距离 | 增长 → 收报停滞 |
| `msgx.pipeline.hole.aged_out.total` | 永久空洞放行次数 | 突增 → ID 序列大量空位 |
| `msgx.pipeline.late_arrival.detected.total` | 迟到到达命中数 | **> 0 表示上游提交确实晚于水位推进,需要与库方对契约** |
| `msgx.pipeline.job.heartbeat_age_seconds` | 距上一次作业 tick 完成的秒数(未跑过为 -1) | 持续增长 → 作业线程卡死 |
| `msgx.pipeline.job.last_failure_age_seconds` | 距最近一次作业 tick 失败的秒数(从未失败为 -1) | 配合 `failures.total` 增长判断扫描/历史作业异常 |
| `msgx.pipeline.job.ticks.total` | 作业 tick 完成次数 | 不增长 → 作业停摆 |
+1 -1
View File
@@ -38,7 +38,7 @@
1. 按配置周期、ID 升序、有限批次采集信箱行;扫描谓词以 [design.md](design.md)「收报与水位」为准(按 ID 区间,不以处理标记为谓词)。接收层只入队,不解析业务、不回填已处理标记。
2. 按信箱 ID 幂等建立 PG `PENDING`;重复扫描、并发兼容入队和进程重启都不能重置已有终态。
3. 快路径用持久水位,补偿路径受控重扫遗漏;本批 PG 入队全部确认后才推进水位。补偿可分页推进,不能被已入队但尚未回填的前一批永久挡住。
3. 快路径用持久水位,本批 PG 入队全部确认后才推进水位。
4. PG 不可用或批次中途失败时不改信箱标记;恢复后补建遗漏,记录失败次数与扫描进度。
5. 较小 ID 迟提交、ID 有空洞、兼容入口先入队较大 ID 时,必须遵守经 Q2 确认的发现与顺序协议;不能用“最终会重扫”冒充严格 FIFO。