docs: 精简重复论证与过时流程表述,口径与实现双向对齐

This commit is contained in:
windyboy
2026-09-11 14:26:44 +08:00
parent a84751bf03
commit 632a5fe422
3 changed files with 57 additions and 85 deletions
+25 -40
View File
@@ -30,7 +30,7 @@
分环节的原因是存储边界:信箱与自有 PG 之间的写入不共享事务。“落信”以共享 MySQL 为准,“入队”和“处理完成”以自有 PG 为准;“已回填”与“投递确认”各以外部操作成功和本地确认事实共同判定。后两个环节各自独立重试,都可能单独失败。“投递确认”只表示 Kafka Broker 或其他投递目标已接受,不表示业务消费者已消费。
入队这一环还包含一个容易被忽略的前提:**发现是完整的**。它不由本系统单独保证,而依赖信箱 ID 单调与最大提交时延两条上游承诺(Q2);承诺不成立时,本系统只能看到"读过的区间",不能证明"读全了"(§5.1)。
"入队"这一环还有一个不由本系统单独保证的前提:**发现是完整的**——依赖 Q2 的两条上游承诺(§5.1)。
## 2. 状态总纲
@@ -54,7 +54,7 @@ MSG_EVENT(自有 PG,投递侧,见 design.md §5
- `PENDING``FAILED` 是处理中的状态,`FAILED` 继续退避重试并占用 FIFO 队头;`SUCCEEDED / SKIPPED / DEAD` 是终态,到达后队列方可推进。
- `DEAD``FAILED` 不是不可逆:经人工批准,指定错误类别的记录可重新置回 `PENDING` 处理(放行范围见 §9 / design.md §6.1)。重放处理的是当前状态,不恢复历史处理顺序。
- **写事务边界**只有处理器产出的业务型终态与航班变更、待发事件、回填意图同处一个 PG 事务;非业务型终态(`MALFORMED / PROTOCOL / SKIPPED / EXHAUSTED`与毒丸升级只写 `PROC_STATE` 一条记录,终态与回填意图同一条语句写入。完整边界见 [design.md](design.md) §3.3 表 1。
- **写事务边界**:业务型终态与航班变更、待发事件、回填意图同处一个 PG 事务;其余终态与毒丸升级只写 `PROC_STATE`终态与回填意图同一条 UPDATE。完整边界见 [design.md](design.md) §3.3 表 1。
- 信箱处理标记写在该事务**之后**,由与终态同行的回填意图驱动重试与超期补写。
## 3. 处理阶段
@@ -72,7 +72,7 @@ MSG_EVENT(自有 PG,投递侧,见 design.md §5
| 阶段 | 输入 | 输出 | 幂等依据 | 失败处理 |
|---|---|---|---|---|
| 回填扫描(每 30 秒,**调度周期 ≠ 完成时限**) | 终态 ∧ 未确认标记 ∧ 未放弃 ∧(退避到期 ∨ 已达 `NOW R`) | 追平标记,或判定放弃自动重试 | 消息 ID(仅空标生效) | 退避 30 秒起步、封顶 15 分钟;越过 `R` 后每轮都试;**确认行不存在**或达尝试上限则停止自动重试(可人工恢复) |
| 回填扫描(每 30 秒,**调度周期 ≠ 完成时限**) | 扫描谓词见 §5.2 | 追平标记,或判定放弃自动重试 | 消息 ID(仅空标生效) | 退避、超期强制补写与放弃口径见 §5.2 |
主泵**不做**回填:终态与回填意图由同一条 UPDATE 落库,回填一律由扫描驱动(跨库写不能占用 FIFO 关键路径)。
@@ -99,7 +99,7 @@ MSG_EVENT(自有 PG,投递侧,见 design.md §5
└─ 回填扫描(每 30 秒)按表 2 的谓词重试,越过 R 后强制补写
```
领域决策逻辑不执行 I/O。Processor 作为事务协调器:业务型终态在持有 `PIPELINE_LOCK` 的事务内写入自有 PG;非业务型终态与毒丸升级只写 `PROC_STATE` 单条记录。所有外部副作用(读信箱原文、写信箱标记)都发生在 PG 事务边界之外跨库不共享事务。
领域决策逻辑不执行 I/O;业务型/非业务型终态的写入边界与全部外部副作用(读信箱原文、写信箱标记)都在 PG 事务之外跨库不共享事务。
## 4. 故障恢复
@@ -111,19 +111,13 @@ MSG_EVENT(自有 PG,投递侧,见 design.md §5
| 水位卡在空洞 | `INBOX_CURSOR.HOLE_SINCE` 有值且未超过 `max-commit-delay` | 等待;超期后放行空洞本身并继续推进(§5.1) |
| 事务执行中 | PG 无该消息终态 | 事务整体回滚,按 `PENDING` 重新处理 |
| 事务已提交、标记未写 | 终态行仍持有回填意图(`BACKFILL_NEXT_AT` 非空) | 仅补写标记;业务处理结果保持不变 |
| 回填意图登记失败(与业务同事务) | 事务未提交 | 同"事务执行中",不构成独立窗口 |
| 标记写入中途 | 标记仍为空 | 重新写入;重复写入同一值无副作用 |
| 兼容入口已入队、水位未追平 | PG 已有该 ID 的记录 | 轮询读到该行时主键幂等,水位照常推进;顺序风险见 §5.1/§12 |
| 回填时信箱行已不存在 | 写入 0 行且信箱行不存在 | **立即放弃自动重试**(原因 `MISSING_ROW`)并告警;**放弃 ≠ 标记已确认**,仍需人工对账(§5.2) |
| `RECEIVED_AT` 为 NULL | 终态行无标记且无超期判据 | 仅由退避重试保证;【目标】以入队时间兜底(§5.2/§12) |
| 投递目标已接受、`SENT` 未置 | 事件仍 `PENDING` | 允许重发,消费方按事件身份去重 |
两个窗口已随"终态与回填意图同体同行"消除(口径与 design.md §10「事务与外部副作用」一致),不再是缺口:
1. **提交后回填前崩溃**:回填意图与处理终态是同一条记录的同一次写入、同一个事务;重启后扫描按该意图继续补写。
2. **回填意图二次落账失败**:不存在第二处落账——意图就在终态行上。
仍属未闭环的有两处:回填本身在跨库单写期间的持续失败,由退避重试与 §5.2 的超期期限 `R` 兜底;以及 §5.2 的"信箱行已不存在"没有终态(登记于 §12)。两者均登记在 design.md §10 与 user-stories.md US-09。
两个历史窗口(提交后回填前崩溃、回填意图二次落账失败)已随"终态与回填意图同体同行"消除——意图不存在第二处落账(口径与 design.md §10「事务与外部副作用」一致)。仍属未闭环的只有回填本身在跨库单写期间的持续失败:由退避重试与 §5.2 的超期期限 `R` 兜底,登记于 design.md §10 与 user-stories.md US-09"信箱行不存在"已有终态(§5.2 放弃语义)。
## 5. 消费水位、超期补写与历史积压
@@ -162,7 +156,7 @@ MSG_EVENT(自有 PG,投递侧,见 design.md §5
| 迟到检测(只读,阶段 0) | 复查**被放行的空洞 ID** 是否后来真的出现在信箱 | 进程内监视队列 + `existingIds` 批量存在性检查(`late-detect-period`) | 【现状】已实现:只计数与告警,**不补入队** |
| 补偿扫描(阶段 1) | 发现"提交晚于水位推进"的迟到行并**安全补入队** | 按周期重扫 `W` 之前一个窗口(宽度由最大提交时延决定)内的 ID 区间 | 【缺口】未实现 |
【缺口】在补偿扫描交付之前,"较小 ID 迟提交"**没有补入队机制**:快路径只读 `ID > W`,一旦水位越过某个 ID,该 ID 之后到达的消息永远不会被发现。阶段 0 的**只读迟到检测**已实现——监视"被放行的空洞 ID"是否后来真的出现,命中即计数(`msgx.pipeline.late_arrival.detected.total`)并告警,把原先的**静默丢失**变成可观测事实;但它**不补入队**,因此当前设计仍**不能对外声明"迟到不越序"**[design.md](design.md) §8/§10)。检测是进程内尽力而为(重启丢失监视),且只覆盖"曾被放行过的空洞 ID"——这已足够:水位只会越过连续存在的行与被判定为空洞的 ID。
【缺口】在补偿扫描交付之前,"较小 ID 迟提交"**没有补入队机制**:快路径只读 `ID > W`,一旦水位越过某个 ID,该 ID 之后到达的消息永远不会被发现。阶段 0 的**只读迟到检测**已实现——监视"被放行的空洞 ID"是否后来真的出现,命中即计数(`msgx.pipeline.late_arrival.detected.total`)并告警,把原先的**静默丢失**变成可观测事实;但它**不补入队**,因此当前设计仍**不能对外声明"迟到不越序"**[design.md](design.md) §8/§10)。检测是进程内尽力而为(重启丢失监视;监视队列有上界,被放行 ID 超量后只保留后段),且只覆盖"曾被放行过的空洞 ID"——这已足够:水位只会越过连续存在的行与被判定为空洞的 ID。
**水位与其余两条写路径的关系**
@@ -177,20 +171,22 @@ MSG_EVENT(自有 PG,投递侧,见 design.md §5
### 5.2 超期标记补写
所有处理终态都只依赖消息 ID 执行回填;报文残缺或缺少 META 不妨碍正常回填。本节规定回填的种结果"长期失败"时的超期兜底。
所有处理终态都只依赖消息 ID 执行回填;报文残缺或缺少 META 不妨碍正常回填。本节规定回填的种结果"长期失败"时的超期兜底,以及 `R` 对重放窗口的实际作用
**处理规则**:已达 `PROC_STATE` 终态、且接收时间超期(信箱 `DATE_RECEIVED`,落库为 `PROC_STATE.RECEIVED_AT`;判据 `RECEIVED_AT < NOW R`)仍无标记的信箱行,由回填通道补写一个库方认可的"已处理"类标记。
**扫描谓词**(与实现一一对应):
```text
STATE ∈ {SUCCEEDED, SKIPPED, DEAD} -- 终态
AND BACKFILL_AT IS NULL -- 标记尚未确认
AND ( BACKFILL_NEXT_AT IS NULL -- 异常兜底:终态行没有退避时间
OR BACKFILL_NEXT_AT ≤ NOW -- 退避到期
OR ( RECEIVED_AT IS NOT NULL -- 接收时间可用
AND RECEIVED_AT < NOW − R ) ) -- 已达超期期限,覆盖退避
ORDER BY MSG_ID ASC LIMIT backfill-batch
STATE ∈ {SUCCEEDED, SKIPPED, DEAD} -- 终态
AND BACKFILL_AT IS NULL -- 标记尚未确认
AND BACKFILL_ABANDONED_AT IS NULL -- 未放弃(放弃行可人工恢复)
AND ( BACKFILL_NEXT_AT IS NULL -- 异常兜底:终态行没有退避时间
OR BACKFILL_NEXT_AT NOW -- 退避到期
OR ( RECEIVED_AT IS NOT NULL -- 接收时间可用
AND RECEIVED_AT < NOW R ) ) -- 已达超期期限,覆盖退避(一旦成立恒成立)
ORDER BY BACKFILL_ATTEMPTS ASC, MSG_ID ASC -- 公平轮转,永久失败行不占满批次
LIMIT backfill-batch
```
**回填的可能结果**
@@ -210,17 +206,9 @@ ORDER BY MSG_ID ASC LIMIT backfill-batch
- 重放窗口的保护只能来自下面两条路之一,且 Q7/Q9 的确认是**阻塞性前提**,不是"调大 `R`"就能覆盖的参数问题:
- **约定保留期(目标前提)**:库方按"标记 + 保留期 `R_keep`"清除(打标本身不触发清除,§6),且 `R_keep ≥ 人工重放期限 + 人工处置期限`。此时 `R` 只需满足 `R ≤ R_keep`,与重放窗口无关。
- **另设原文保留机制**:若库方的清除语义是"一旦打标即可清除"(未确认),则**增大 `R` 无效**——当天进入终态、当天打标的消息会被当天清除,即使 `R = 30 天`。此时必须另行约定保留期,或引入独立的原文保留通道(例如由库方把原文归档到 `CMINMSGS_HST` 后供重放读取);该通道**尚未设计**(§12 G10)。
- 撤回原先"打标即清除语义下需 `R ≥ 重放期限 + 处置期限`"的表述:它是错误推论,因为**回填由扫描驱动、不等 `R`**——打标时刻与 `R` 本来就是解耦的,`R` 无法推迟打标。
- 中间态(`PENDING` / `FAILED`)不适用本规则:处理未完成时不打标,也不允许被清除。
- 补写值仅限于库方认可的 legacy 值集(Q7);死信、业务重复等内部原因记录在 `PROC_STATE` 与审计日志,不在信箱新增枚举。
- 补写只针对空标记;已有值不回撤、不覆盖,重复执行无副作用。
- `RECEIVED_AT` 为 NULL(上游未写 `DATE_RECEIVED`)时,超期分支不成立,`R` 兜底**不生效**;该行只能靠退避(封顶 15 分钟)反复重试。【目标】接收时间缺失时以入队时间兜底。
- 判据不依赖独立待办表:回填意图与处理终态同行(§2/§4)。
- **越过 `R` 之后**:超期分支一旦成立便恒成立,该行**每轮扫描都会被重试**,退避被覆盖。不过永久失败不会无限重试:**确认行不存在**立即放弃,暂时性故障到 `backfill-max-attempts` 后也停止自动重试(两类都告警,且可人工恢复)。
- **扫描周期 ≠ 完成时限**:30 秒只是**调度周期**。`JobRunner` 串行执行 `backfill.sweep` 与历史作业后才等待 30 秒,批次积压、单行调用超时与历史作业耗时长都会延长实际回填延迟;只有在"正常无积压"时实际延迟才近似等于扫描周期。因此任何"标记延迟 ≤ 30 秒"的表述都不成立,**回填完成目标是独立指标**(§5.3 验收口径、OPS-2),需要硬时限时必须另行补齐容量、作业隔离与故障条件。
- **回填饥饿(已修)**:扫描改为**公平轮转**(`ORDER BY BACKFILL_ATTEMPTS ASC, MSG_ID ASC`)并排除已放弃的行,因此"最旧的一批永久失败行占满批次、后面的记录永远轮不到"不再成立。历史缺口见 §12 G9(已关闭)。
该规则保证两件事成立:收报表不被永不回填的记录占满(Q2 中"积压挡批"场景)、`R``R_keep` 的约束关系可核验(§5.2/§6/§9)。**它仍不保证"§6 的清除边界能在有限时间内达到"**:放弃行永远不满足"边界内全部行已打标",而"打标即清除"语义(§12 G10)会让该前提根本不成立。
### 5.3 历史积压消息的处理方案
@@ -233,14 +221,12 @@ ORDER BY MSG_ID ASC LIMIT backfill-batch
1. 入队:`InboxPoller` 按升序有限批次将历史行全部建为 `PROC_STATE(PENDING)`,水位随之推到积压末端(连续推进以 §5.1 的 Q2 承诺为前提)。此阶段只写自有 PG,不触碰信箱标记。
2. 处理:主泵按最小未完成 ID 顺序消化。顺序、阈限与日常完全相同:不加速、不分流、不走旁路。积压期间到达的实时消息 ID 更大,自然排在积压之后。
**入队与处理可以并发**:两者由不同线程驱动,FIFO 由"队头取最小未完成 `MSG_ID`"保证,并发不会破坏顺序。因此"先入队后处理"是**可选的运维规程**(例如先手动跑一段只收报的窗口,便于提前把水位推到完整上界),**不是正确性前提**,系统也不需要提供"只入队"模式。
这样安排的收益:入队廉价、处理昂贵;即使并发,入队阶段的中断恢复也只涉及重扫(§4 第一行),水位能尽早到达完整上界,快路径与补偿扫描窗口立即生效。
**入队与处理可以并发**:两者由不同线程驱动,FIFO 由"队头取最小未完成 `MSG_ID`"保证。因此"先入队后处理"是**可选的运维规程****不是正确性前提**,系统也不提供"只入队"模式;入队阶段的中断恢复只涉及重扫(§4 第一行)
**第三步:等待过程中的四个边界。**
- 不插队:主泵按 FIFO 消化,积压期间到达的实时消息排在积压之后。本设计不允许并行队头,也不允许实时通道跳过积压。
- 不失控:队头滞留上限(`msgx.pipeline.head-deadline`,当前 10 分钟)对积压同样生效;队头长期失败按既有规则转 `DEAD(EXHAUSTED)` 并告警,不会因积压而延长容忍判据以首次处理时写入的 `PROCESSING_STARTED_AT` 为稳定起点、经可注入 `Clock` 判定(design.md §3.2、§10);长期积压与人工重放的期限口径仍由 Q6 定案。
- 不失控:队头滞留上限(`msgx.pipeline.head-deadline`,当前 10 分钟)对积压同样生效;队头长期失败按既有规则转 `DEAD(EXHAUSTED)` 并告警,不会因积压而延长容忍判据口径见 design.md §3.2);长期积压与人工重放的期限口径仍由 Q6 定案。
- 报文类型的处理方式不变:旧的全量日计划(`SCHD-DNLD`)按最新一份**合并**即可收敛(缺席不删、缺失字段保留;字段级冲突见 flight-state.md §3.1 与 Q13),但仍逐条执行;动态增量(`FLOP-*` / `FDEL` / `ADFT`)持有时序语义,必须逐条。
- 可放弃但必须留痕:摸底批中经库方确认"不再处理"的行,处置方式为——`PROC_STATE``SKIPPED` 并记录跳过原因,到达终态后走 §5.2 通道补写标记;本方案不支持任何"整段 DELETE"的快速通道。
@@ -255,7 +241,7 @@ ORDER BY MSG_ID ASC LIMIT backfill-batch
**方案 A:按日分区(首选)**,适用于库方可以为表增加分区的场合。
1. `CMINMSGS``DATE_RECEIVED` 建立日粒度 RANGE 分区;
2. 某分区到达保留期时,确认该分区全部行已持有处理标记(§5.2 只在回填可持续成功时保证该条件;存在回填饥饿或"打标即清除"语义见 §12 G9/G10);
2. 某分区到达保留期时,确认该分区全部行已持有处理标记(§5.2 只在回填可持续成功时保证该条件;放弃行与"打标即清除"语义都会破坏该前提,见 §12 G10);
3. 将该分区复制入历史表:`INSERT INTO CMINMSGS_HST SELECT``NOT EXISTS` 判重);
4. `TRUNCATE / DROP PARTITION` 执行清除:DDL 级操作,无行锁竞争,页外大字段(`CMINMSGS_CLOB_MSG` 列)整块释放;碎片与 binlog 影响待现场验证。
@@ -274,7 +260,7 @@ ORDER BY MSG_ID ASC LIMIT backfill-batch
方案共同前提:
- **方案要求库方按"标记 + 保留期"清除**(打标本身不触发清除):处理标记只是可清除的**必要条件**,触发条件是"到达保留期 `R_keep`" ∧ "边界内全部行已持有处理标记"。该语义属 Q7/Q9 确认范围;若确认结果是"打标即可清除",则**本节前提不成立,且增大 `R` 无法补救**(原因见 §5.2:回填不等 `R`,打标时刻与 `R` 解耦),必须另行约定保留期或引入独立原文保留通道(§12 G10)。
- **方案要求库方按"标记 + 保留期"清除**(打标本身不触发清除):处理标记只是可清除的**必要条件**,触发条件是"到达保留期 `R_keep`" ∧ "边界内全部行已持有处理标记"。该语义属 Q7/Q9 确认范围;若确认结果是"打标即可清除",则**本节前提不成立,且增大 `R` 无法补救**(原因唯一见 §5.2),必须另行约定保留期或引入独立原文保留通道(§12 G10)。
- 执行清除时,边界内不存在未打标记的行;未达终态的行顺延至处理完成后清除(§5.2 仅对终态行补标)。
- 时间比较与换算统一采用机场时区 Asia/Shanghai 及明确的类型转换(口径同 user-stories.md §6)。
- 保留期 `R_keep` 的下界(**本节是唯一表述处**):`R_keep ≥ max(人工重放期限 + 人工处置期限, 审计期限, 回填重试上限)`——**仅在"标记 + 保留期"清除语义下**,这是"重放窗口内原文仍在"的**唯一保证来源**。报文在 CIIMS 的 `Expiry`480 分钟量级,`SIS_AODB_RMS-V0.1.md` §3.16)可作为原文保留期的参照,但它是报文有效期,不等于本处所需的保留期,也不能用于推导 §5.1 的可见性时延。
@@ -308,9 +294,9 @@ ORDER BY MSG_ID ASC LIMIT backfill-batch
## 9. 重放与原文可用性
- 可重放的错误类别为 `CODEC_ERROR / UNSUPPORTED / INFRA / EXHAUSTED` 四类(以 design.md §6.1 为准);重放处理当前状态,不恢复历史顺序。
- 重放的前提是信箱原文仍可读取。**该前提在"标记 + 保留期"清除语义下才能由配置满足**`R_keep` 必须覆盖(人工重放期限 + 人工处置期限)(§6。若库方是"打标即清除",则需独立的原文保留通道(§5.2§12 G10)——**增大 `R` 无效**
- 重放的前提是信箱原文仍可读取。该前提在"标记 + 保留期"清除语义下由配置满足`R_keep` 覆盖重放与处置期限§6"打标即清除"语义下的处置唯一见 §5.2§12 G10。
- 确认信箱行或原文已缺失时,按 `MALFORMED → DEAD` 处理;共享库超时、连接失败等暂时性读取异常按 `INFRA` 退避重试,不得伪装成原文缺失。若原文缺失源于库方违反保留契约提前清除,按契约违例走运维追责通道(契约条款见 Q7/Q9);该消息的死信处置本身不变。
- **回填阶段的"信箱行缺失"是另一回事**(§5.2):它表示"本系统已经处理完、却找不到要标记的那一行"。此时不改变处理终态,也不重新处理业务;需要区分"该 ID 本就属于被判定的永久空洞"与"行被提前清除"。【缺口】当前无终态与告警,见 §12
- **回填阶段的"信箱行缺失"是另一回事**(§5.2):它表示"本系统已经处理完、却找不到要标记的那一行"。此时不改变处理终态,也不重新处理业务;需要区分"该 ID 本就属于被判定的永久空洞"与"行被提前清除"。处置按 §5.2:确认 `MISSING_ROW` 即放弃自动重试并告警,人工对账后才能重排回填
## 10. 开放问题索引
@@ -351,13 +337,12 @@ Q2、Q6Q14 的完整登记见 [user-stories.md](user-stories.md) §6 的 Q
| 编号 | 缺口 | 影响 | 相关章节 |
|---|---|---|---|
| G1 | **窗口补偿扫描未实现**(阶段 0 已交付) | 快路径只读 `ID > W`;水位越过某个 ID 后,该 ID 之后到达的迟到消息永远不会被补入队。**已实现只读迟到检测**(`msgx.pipeline.late_arrival.detected.total` + 告警),把静默丢失变为可观测;**补入队(阶段 1)仍未实现**,因此不能声明"迟到不越序" | §5.1、§4 |
| G2 | ~~**兼容入口不参与水位**~~(**已修**) | 兼容入口登记的行超出水位,主泵只领 `msgId ≤ W`,因此不会越过尚未入队的较小 ID;代价是需等水位追平(且收报轮询必须运行)。收报侧事实与端到端验收均有用例 | §5.1、§4 |
| G3 | ~~**回填"信箱行缺失"无终态**~~(**已修 · V4**) | 运行时确认 `MISSING` 即放弃自动重试(原因 `MISSING_ROW`)并告警;暂时性故障达 `backfill-max-attempts` 后也停止自动重试;两者均可人工恢复,且**不等于**标记已确认 | §5.2、§4、§9 |
| G1 | **窗口补偿扫描未实现**(阶段 0 只读检测已交付) | 迟提交的小 ID 无补入队机制,不能声明"迟到不越序";检测覆盖与口径唯一见 §5.1 | §5.1、§4 |
| G4 | **`RECEIVED_AT` 为 NULL 时 R 兜底失效** | 该行只能靠退避重试;V2 只对存量行用 `UPDATED_AT` 兜底,新行没有 | §5.2、§4 |
| G5 | **毒丸升级不在 `MessageLifecycleGate` 内** | 与人工重放并发时,存在"先标 DEAD 并写标记、再被重放拨回 `PENDING`"的窗口。**注意**:回填与重放**共用同一个 gate 单例**这一点已由装配断言守住(`PipelineSmokeTest`),但毒丸路径仍在 gate 之外 | §2、§5.3design.md §3.2/§6.1 |
| G6 | **"预计消化时长"指标无实现定义** | §5.3 验收口径的第三项指标尚无算法与实现(已降级为可推算项) | §5.3 |
| G7 | **`max-commit-delay` 默认值缺少依据** | 库方尚未给出"ID 分配 → 事务可见"的时延上界;5 分钟只是占位假定值,**不能由 SIS 报文 `Expiry` 推导**(§5.1)。取值无依据时可能大量误判永久空洞 | §5.1、§6 |
| G8 | **Q 编号条款尚未与库方书面确认** | 当前取值均为假定,见 §10 | §10 |
| G9 | ~~**回填扫描无公平轮转、无永久失败隔离**~~**已修 · V4** | 改用 `ORDER BY BACKFILL_ATTEMPTS, MSG_ID` 公平轮转并排除已放弃行,新行不会再被最旧的一批永久失败行饿死 | §5.2、§3、§6 |
| G10 | **"打标即清除"语义下没有原文保留通道** | 若库方清除由打标触发,则**回填成功后当天打标**会让原文当天即可被清除,增大 `R` 无效 → 重放窗口失去保护。需另行约定保留期,或引入独立原文保留通道(如库方归档 `CMINMSGS_HST` 供重放读取);该通道尚未设计 | §5.2、§6、§9 |
已关闭(现行行为并入正文,不再列为缺口):G2 兼容入口与水位领取(§5.1)、G3 回填缺失行放弃语义(§5.2)、G9 回填扫描公平轮转(§5.2 谓词)。