docs(acm2-75): 按需求与架构收口契约和规范
补齐接口契约的入站、Redis 与出站边界,规范对齐已定语义并作废过期条款;Kafka 生产端约束编号改为 D2。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+20
-19
@@ -14,7 +14,7 @@
|
||||
|
||||
| 术语 | 语义 |
|
||||
|---|---|
|
||||
| 扫描谓词 | 信箱读取条件「处理标记为空」(`INV-2b`);本系统不以 ID 区间或水位作为消费边界。 |
|
||||
| 扫描谓词 | 信箱读取条件「处理时间为空」(`INV-2b`);本系统不以 ID 区间或水位作为消费边界。 |
|
||||
| 队头 | 最小的未完成消息(`PENDING` 与 `FAILED` 都占位)。 |
|
||||
| 终态 | `SUCCEEDED` / `SKIPPED` / `DEAD`;到达后队列方可推进。 |
|
||||
| 回填意图 | 「还欠一次信箱标记」的持久化事实,与终态同一条语句落库,且发生在 Redis 投影写成功之后(`INV-23`)。 |
|
||||
@@ -30,7 +30,7 @@
|
||||
| `REF_MASTER` | SIS 消息提供的静态参考数据与资源状态(目标表) | `(RTYPE, RKEY)` 唯一;`RTYPE` 类别、合并语义与资源状态见「静态参考数据」;取数路径见 [requirements.md](requirements.md) `US-13`。 |
|
||||
| `FLIGHT_SCHD` | 航班标量及单值异常字段 | `FLID` 主键;`OPERATION_DAY` 一经确定不可变;版本与最近消息 ID 用于追踪。变长集合存于资源明细表与 `FLIGHT_ROUTE_POINT`,规则见「航班域」。 |
|
||||
| `SCHD_SNAP_LOG` | 日计划处理留痕 | 只追加、可重建,不参与状态决策;保留期见 [reference.md](reference.md)。 |
|
||||
字段与索引以 `src/main/resources/db/migration/` 的迁移链为准(Oracle 11g 目录为占位,未接入 Flyway)。报文原文仍从共享信箱读取,原文保留期必须满足 `C-7`;清除前提、保留期下界与处理标记值集见 `C-5`~`C-12`。
|
||||
字段与索引以 `src/main/resources/db/migration/` 的迁移链为准(Oracle 11g 目录为占位,未接入 Flyway)。报文原文仍从共享信箱读取,原文保留期必须满足 `C-7`;处理标记值集见 `C-5`,清除前提与保留期下界见 `C-6`~`C-9`、`C-11`。
|
||||
|
||||
|
||||
## 2. 消息、身份与决策
|
||||
@@ -66,10 +66,10 @@
|
||||
|
||||
### 4.1 收报流程
|
||||
|
||||
`InboxPoller` 按配置周期查信箱中「处理标记为空」的行,按编号升序、每批有上限,在自有 PG 登记 `PENDING`(`INV-2b`)。每轮:
|
||||
`InboxPoller` 按配置周期查信箱中「处理时间为空」的行,按编号升序、每批有上限,在自有 PG 登记 `PENDING`(`INV-2b`)。每轮:
|
||||
|
||||
1. 信箱不可读时记日志、等下一轮——这是基础设施失败,不能当成「没有新消息」。
|
||||
2. 取「处理标记为空」的行的升序前 `PARAM:msgx.pipeline.claim-batch` 条。
|
||||
2. 取「处理时间为空」的行的升序前 `PARAM:msgx.pipeline.claim-batch` 条。
|
||||
3. 在同一个 PG 事务内对每一行 `insertIfAbsent(MSG_ID, RECEIVED_AT, ENQUEUED_AT)`;主键冲突表示已登记(重复扫描与兼容入口并发都安全),不计入、不报错。
|
||||
4. 提交。已打标的行不再出现在扫描结果里;终态但未回填的行会被重复读到,按已有记录幂等跳过。
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
### 4.3 兼容 HTTP 入口
|
||||
|
||||
`POST /cminmsgs/send` 把报文写入共享信箱(处理标记为空),效果与上游投递一致:由收报扫描发现、登记、处理。客户端失败重试可能再次写信箱,业务身份去重仍然必需。响应语义见 `C-28`。
|
||||
`POST /cminmsgs/send` 把报文写入共享信箱(处理时间为空),效果与上游投递一致:由收报扫描发现、登记、处理。客户端失败重试可能再次写信箱,业务身份去重仍然必需。响应语义见 `C-28`。
|
||||
|
||||
### 4.4 单实例
|
||||
|
||||
@@ -185,7 +185,7 @@ LIMIT PARAM:msgx.pipeline.backfill-batch
|
||||
| 信箱行不存在 | 写入 0 行且信箱行不存在 | **立即放弃自动重试**(原因 `MISSING_ROW`)并告警。终态行存在而信箱行不存在,只可能是该行在入队后被删除(永久空洞 ID 从不入队,不会进入本扫描) |
|
||||
| 暂时性故障持续超期 | 超时 / 连接失败持续到 `R` 仍未打标 | **停止自动重试**(原因 `TRANSIENT_DEADLINE`)并告警;`R` 之前只退避重试,**不按尝试次数放弃**;保留人工恢复能力 |
|
||||
|
||||
**放弃 ≠ 标记已确认**:放弃行不写 `BACKFILL_AT`,因此不满足 `C-8` 的清除前提,库方不得据此清除;放弃清单需人工对账确认后才可用于清除判定。
|
||||
**放弃 ≠ 标记已确认**:放弃行不写 `BACKFILL_AT`,处理标记仍为空,「行最终都有标记」因此不能对外承诺(`CLM-4`);放弃清单能否作为清除判定依据,属未确认的清除协议(`C-8`;见 `Q7`/`Q9`)。
|
||||
|
||||
### 6.3 `R` 的作用
|
||||
|
||||
@@ -196,15 +196,16 @@ LIMIT PARAM:msgx.pipeline.backfill-batch
|
||||
|
||||
放弃判据用**时间**而不是**尝试次数**:固定次数不能稳定表达允许的故障持续时间,因此按 `R` 判断放弃,`PARAM:msgx.pipeline.backfill-max-attempts` 只用于告警。
|
||||
|
||||
关于「最终一定打标」,准确表述是三段,缺一不可:
|
||||
关于「最终一定打标」,本系统能保证的只有两段:
|
||||
|
||||
1. 退避重试(`R` 之前不放弃);
|
||||
2. 到 `R` 仍失败则停止自动重试、告警,进入放弃清单,保留人工恢复(`reopen`);
|
||||
3. `C-8` 允许以「放弃清单 + 人工确认」作为清除判定,避免一行永久卡住整个分区。
|
||||
2. 到 `R` 仍失败则停止自动重试、告警,保留人工恢复(`reopen`)。
|
||||
|
||||
第三段「库方以放弃清单作为清除判定」未确认,因此「最终一定打标」当前不可承诺(`CLM-4`)。
|
||||
|
||||
两个边界要说清:`MISSING_ROW`(信箱行不存在)是**确定性结论**,立即放弃,不受 `R` 保护;`R` 只要求 `R ≤ R_keep`,原文保留期的唯一约束来源是 `C-7`。
|
||||
|
||||
若库方清除语义是「打标即可清除」,则清除前提(`C-6`~`C-8`)不成立,必须与库方另定保留期;增大 `R` 无效。
|
||||
库方的清除语义未确认(`C-6`、`C-8`;见 `Q7`/`Q9`):若为「打标即可清除」,`C-7` 的保留期下界不成立,必须与库方另定;增大 `R` 无效。
|
||||
|
||||
## 7. 日计划快照与请求匹配
|
||||
|
||||
@@ -248,7 +249,7 @@ PENDING → SENT → DONE
|
||||
|
||||
发送确认后才标记 `SENT`,失败记录次数并按退避推后,达到上限转 `DEAD`(记录保留作 DLQ)。所有外部调用需要有界超时,避免阻塞投递线程。
|
||||
|
||||
投递是至少一次:Broker 或其他目标已接受但本地未标记成功时可能重发;目标端接受不等于业务消费者已消费。Kafka 生产约束沿用 `D3`,生产者幂等不替代应用层事件去重。
|
||||
投递是至少一次:Broker 或其他目标已接受但本地未标记成功时可能重发;目标端接受不等于业务消费者已消费。Kafka 生产约束沿用 `D2`,生产者幂等不替代应用层事件去重。
|
||||
|
||||
### 8.2 `schd` 聚合
|
||||
|
||||
@@ -285,7 +286,7 @@ PENDING → SENT → DONE
|
||||
|
||||
| 中断位置 | 重启后的判定 | 恢复动作 |
|
||||
|---|---|---|
|
||||
| 已落信、未入队 | 信箱行处理标记为空且 PG 无记录 | 重扫补建登记记录 |
|
||||
| 已落信、未入队 | 信箱行处理时间为空且 PG 无记录 | 重扫补建登记记录 |
|
||||
| 事务执行中 | PG 无该消息终态 | 事务整体回滚,按 `PENDING` 重新处理 |
|
||||
| 领域事务已提交、Redis 写失败或终态未提交 | 该消息无终态(`PENDING`),仍占队头 | 整条消息重处理:投影按当前完整态重写,领域变更依赖逐类幂等(`INV-20b`,`G-FLOP-IDEMPOTENT`),已提交结果不回滚(`INV-16`) |
|
||||
| 事务已提交、标记未写 | 终态行仍持有回填意图 | 仅补写标记;业务处理结果保持不变 |
|
||||
@@ -301,15 +302,15 @@ PENDING → SENT → DONE
|
||||
|
||||
**通则**(对本系统所有持久对象适用)
|
||||
|
||||
- **时间不构成清除依据**:到期只是必要条件,**终局证据才是充分条件**(共享库见 `C-8`,航班见 `INV-28`)。
|
||||
- **证据不随清除消失**:清除所依赖的证据(如 `C-8` 引用的回填放弃清单,本期承诺见 `C-16`)在其覆盖的信箱边界被清除前必须保持可查。
|
||||
- **时间不构成清除依据**:到期只是必要条件,**终局证据才是充分条件**(航班见 `INV-28`;共享库的清除依据属未确认的清除协议,见 `C-8` 与 `Q9`)。
|
||||
- **证据不随清除消失**:回填失败与放弃的记录在其覆盖的信箱行被清除前保持可查(`C-16`)。
|
||||
- **证据缺失或结果不明时按最保守处置**:航班清理为删 0 条(`INV-28`)。
|
||||
|
||||
**逐对象生命周期**(保留期取值一律见 [reference.md](reference.md))
|
||||
|
||||
| 对象 | 终局判据 | 归档目标 | 清除证据 | 执行方 | 偏差 |
|
||||
|---|---|---|---|---|---|
|
||||
| 共享信箱 `CMINMSGS` 原文 | 处理标记 / 回填放弃清单 | — | `C-8` | 库方 | 契约未确认(`Q7`/`Q9`) |
|
||||
| 共享信箱 `CMINMSGS` 原文 | 处理标记 | — | 待确认(`Q9`) | 库方 | 契约未确认(`Q7`/`Q9`) |
|
||||
| `FLIGHT_SCHD` + 资源明细 | 判史规则 | 历史存储 | 历史写入确认 + 版本复查 | 我们 | — |
|
||||
| 航班历史存储 | 保留期 | — | — | 我们 | `G-FLIGHT-HIST-RETENTION` |
|
||||
| `SCHD_SNAP_LOG` | 保留期 | 无(本地可重建) | 无 | 我们 | — |
|
||||
@@ -321,12 +322,12 @@ PENDING → SENT → DONE
|
||||
|
||||
候选 = 终态 **且** 回填已了结 **且** 终局后超过保留期(基准是 `UPDATED_AT`:终态与了结都推进它,了结后不再更新)。两处不可省:
|
||||
|
||||
- **回填已了结** = `BACKFILL_AT` 非空,或已放弃 **且经人工对账**。放弃行不写标记,是 `C-8` 的清除授权证据,未对账前不得删除。
|
||||
- **回填已了结** = `BACKFILL_AT` 非空(`INV-25`)。放弃行不写标记,按未了结保留,不参与删除。
|
||||
- **写入前复查** = `DEAD` 可被人工重放改回 `PENDING`。人工重放走 `MessageLifecycleGate`、不取 `PIPELINE_LOCK`,因此该锁不构成复查依据:删除在同一事务内按候选时的 `STATE` 条件执行;影响 0 行即整体回滚、该行跳过。重放先一步改回 `PENDING` 时谓词不匹配,天然互斥。批量删除不得持 `PIPELINE_LOCK`——那会阻塞主泵 FIFO,与「作业不使到期消息饥饿」冲突。
|
||||
|
||||
清理范围只含「终态且已回填」;保留期内同身份去重成立(`INV-9`),保留期过后同身份消息按新消息处理(去重记忆期 = 保留期,见 [specification.md](specification.md)「契约数值」)。
|
||||
清理范围只含「终态且已回填」;保留期内同身份去重成立(`INV-9`)。
|
||||
|
||||
**时间常数排序**:`R ≤ R_keep`、放弃清单可见期 ≥ `R_keep` 两个下界关系的定义与理由见 [specification.md](specification.md)「契约数值」,本文件不复述。只补一条实现口径:保留期计的是**终局之后**的时间,不是入队之后——终态行未了结回填时不进入候选。
|
||||
**时间常数排序**:`R ≤ R_keep` 的定义与理由见 [specification.md](specification.md)「契约数值」,本文件不复述。只补一条实现口径:保留期计的是**终局之后**的时间,不是入队之后——终态行未了结回填时不进入候选。
|
||||
|
||||
**其余清理**
|
||||
|
||||
@@ -334,7 +335,7 @@ PENDING → SENT → DONE
|
||||
- **留痕清理**:`SCHD_SNAP_LOG` 按保留期与 `(SCOPE_END, RECV_AT)` 删除,不依赖历史存储开关。
|
||||
- **出站事件清理**:见「事件清理」。
|
||||
|
||||
共享信箱保留策略由库方管理(`C-5`~`C-12`)。历史写入与删除事件入队之间仍需恢复方案;顺序调用不构成原子提交。
|
||||
共享信箱保留策略由库方管理(`C-6`~`C-9`、`C-11`)。历史写入与删除事件入队之间仍需恢复方案;顺序调用不构成原子提交。
|
||||
|
||||
## 10. 容量假设与设计取舍
|
||||
|
||||
|
||||
Reference in New Issue
Block a user