docs: 二轮审查文档收敛 ACM2-23~27(Kafka/依赖/ResponseDto/STATUS/格式)

This commit is contained in:
windyboy
2026-09-07 14:15:07 +08:00
parent 7ccdd00a31
commit 346929ea5d
5 changed files with 446 additions and 49 deletions
+40 -20
View File
@@ -4,7 +4,8 @@
> `CMINMSGS`)投递的 XML 报文,解析处理后维护 Redis 动态并向 Kafka / 出站信箱投递;
> **非**报文源系统。生产主路径 = **JDBC 轮询**发现新信;HTTP `POST /cminmsgs/send` = compat 写路径。
> 本文对应仓库当前实现,给出模块级设计语义与依据;架构总览见
> [architecture.md](architecture.md)权威架构为 Plane **ACM2-3**实施计划与逐项验收为
> [architecture.md](architecture.md),架构基线为 Plane **ACM2-3**其存储/事务边界由后续
> **ACM2-12** 覆盖;实施计划与逐项验收为
> **ACM2-10U01U30**。文中标注「TODO/未实装」的条目均为已知开放项,不属文档遗漏。
## 0. 系统边界速览
@@ -107,10 +108,13 @@ ErrorClass(两侧共用):
`DATE_PROCESSED IS NULL`);本系统 `ingress` 经 **JDBC 轮询**发现新信(与 legacy
`MsgExchangeRunner.getNewMsgsAfterId` 同语义,1s 节律),自有 PG 入队:
1. `pollNew()`U05`InboxPoller`):JDBC 查共享库 `CMINMSGS_ID > watermark
DATE_PROCESSED IS NULL`(及/或 PG 无对应 PROC_STATE 的补偿重扫);
1. 目标态分两路:快路径按持久化 watermark 查询 `CMINMSGS_ID > watermark AND
DATE_PROCESSED IS NULL`;补偿路径按受控周期重扫“未处理且 PG 无对应 PROC_STATE”的记录。
watermark 仅在本批 PG 入队均已确认后推进。**当前初版** `InboxPoller` 固定 `afterId=0`
每轮全量扫描未处理记录并以 PG 判重,尚未实现上述水位与补偿频控;
2. `procState.insert(id)`:自有 PG 建 PENDING 行入队;本步失败 → 下轮重扫补建;
3. 不解析报文、接收层无唯一约束(I3);`wakePump()` 为 TODO 空操作——主泵 1s 轮询兜底。
3. 不解析报文、接收层无业务 identity 唯一约束(I3);`PROC_STATE.CMINMSGS_ID` 主键负责
轮询重扫幂等。当前无显式 `wakePump()`,主泵 1s 轮询兜底。
**compat 路径(现役 HTTP 写)**`InboxService.accept``POST /cminmsgs/send`=
共享信箱 `insertRaw` + 自有 PG 入队(跨库,非同一事务);「已持久化」响应语义与现役
@@ -133,14 +137,17 @@ ErrorClass(两侧共用):
门禁无单测锁定,见 §7);否则 sleep 至 nextAttemptAt。
4. 正常队头 → `MessageProcessor.processOne`
入口守卫(FAILED 且已 exhausted → DEAD)→ `rawOf` 缺失 → DEAD(MALFORMED) →
decodeMALFORMED→DEAD / CODEC_ERROR→FAILED)→ identity 首绑
`tryBindIdentity` 失败 → SKIPPEDI3)→ Schd DNLD → `SnapshotFlow` →
decodeMALFORMED→DEAD / CODEC_ERROR→FAILED)→ ignoreMsg 匹配(LDM/REGN/RSTA/EROR
命中→SKIPPED,回填遵循 US-09;当前未实装)→ identity 首绑
`tryBindIdentity` 失败 → SKIPPEDI3)→ Schd RESP/DNLD → `SnapshotFlow`ACM2-16 定案:
DNLD 与 RESP 均走 SnapshotFlowRESP 成功后在同事务完成匹配开放 RQFD 的 `REQ_TRACK→DONE`
迟到或无匹配 RESP 严禁更新快照,直接转 SKIPPED 并审计)→
其余 → `Handler.decide(redis.hgetAllFlightInfo(), msg)` →
阶段 ARedis 先写(I2 happens-beforeTODO redisApply)→ 自有 PG 事务 2
MSG_EVENT 插入 + PROC_STATE→SUCCEEDED(同库原子,@Transactional);
CMINMSGS 回填(DATE_PROCESSED/STATUS)为共享信箱**外部回填**:目标态为 PG 提交后
异步/补偿执行,失败重试+告警(最终一致,ACM2-12);**当前实现为同步内联占位**
`insertAll → backfillOnSuccess → SUCCEEDED`,无 `@Transactional`U05 定案并改)
CMINMSGS 回填(DATE_PROCESSED/STATUS)为共享信箱**外部回填**ACM2-19 定案):
PG 事务提交后异步触发执行,持久化补偿、失败退避重试+告警、影子禁写;全部终态
SUCCEEDED / ignore SKIPPED / duplicate SKIPPED / DEAD)均必须回填,非终态禁止回填
5. 异常边界(U08):`processOne` 内 try/catch → `ProcFailure.fail(INFRA)`attempts+1、
退避、达上限 DEAD);`InterruptedException` 恢复中断位后**上抛**loop 仅 catch
`Exception` 作最后防线,`Error` 任其终止进程(异常必可见)。
@@ -157,18 +164,22 @@ ErrorClass(两侧共用):
claim、`lastFlush` 仅成功后推进;达上限整批 DEAD(DLQ)。周期/批上限取参数表
3s / 500)。
- 轮询间隔取参数表(下限 50ms,N18);无 200ms 硬编码。
- **Kafka 生产契约(ACM2-23**:对接 Kafka 2.8+ / 3.x+,生产者强制 `acks=all`、`enable.idempotence=true` 与 `max.in.flight.requests.per.connection=1`;切流前须确认 Broker 支持 `InitProducerId(22)`,严禁非幂等降级;README 不提供生产降级 env。
- 阶段 B(定案 2/D2ACM2-12 缓做):ES 投递成功 → 同线程同步 `insertSync` 删除事件
`deleteOf`Jackson 结构化序列化,refs 可空恒合法 JSON——U14)。当前不启用。
### 3.4 流程 4:日计划快照(`SnapshotFlow`
### 3.4 流程 4:日计划快照(`SnapshotFlow`RESP/DNLD
```
staging(流式解析+整包校验,TODO 阶段2;未实装→FAILED(UNSUPPORTED)
SCHD-RESP / SCHD-DNLD
→ staging(流式解析+整包校验,TODO 阶段2;未实装→FAILED(UNSUPPORTED)
→ 守卫判定:若为 SCHD-RESP,检查开放 RQFDdttm < sentAt 或无匹配/已过期 → 严禁更新快照,转 SKIPPED 并审计)
→ Redis Lua SNAPSHOT_REPLACE(同一 hash 原子「覆盖新代+按代差删」,删除集=旧代flids−新代)
→ gen 版本推进(ACM2-12gen 随 flightInfo 同在 RedisLua 内原子版本 CAS——
目标实现;现 RefDataRepository/putGenIfVersion 为过渡占位)
自有 PG SUCCEEDED
```
→ gen 版本推进(ACM2-12gen 随 flightInfo 同在 RedisLua 内原子版本 CAS
→ 自有 PG 本地事务(原子性):PROC_STATE→SUCCEEDED + RESP 匹配时)REQ_TRACK→DONE + MSG_EVENT 插入
PG 提交后异步触发信箱回填(持久化补偿,ACM2-19)
数据流说明:SCHD-RESP 处理依赖 US-08 已登记的开放 REQ_TRACKUS-06 与 US-08 为单向数据流耦合(US-06 依赖 US-08 登记能力),不构成双向故事依赖(ACM2-24)。
**已知缺口(U09,未定案,ACM2-12 后重设计为 Redis 内协议)**gen 与 Lua/SUCCEEDED 不再
分属两存储即可同原子(全部在 Redis Lua);真正跨存储的窗口收窄为「Lua 已完成、PG SUCCEEDED
@@ -180,8 +191,11 @@ staging(流式解析+整包校验,TODO 阶段2;未实装→FAILED(UNSUPPOR
- HISTORY_SWEEP3:30 清场,I4 同步链):判史 → 同步写 ES → 仅删成功集。
**占位门禁(U10/T07 修订)**ES saveSync 接线前 `pickHistory` 恒空集、删除量恒 0
禁止「全量可删」fail-open 默认;现役五条判史规则 golden 通过后才允许接线。
- ARCHIVE3:00):1 天前且仅终态(SUCCEEDED/SKIPPED/DEAD)迁 CMINMSGS_HST(共享库,
外部副作用;TODO U05 批次)
**阶段归属**:按 ACM2-12 阶段 B 缓做口径标为 DEFERRED,不作为阶段 A 切流门禁;启用前
重新确认“历史链路不变”与阶段 B 投影范围、ES/OpenSearch 产品边界
- ARCHIVE3:00):默认保留 **1 天**(接收时间早于 1 天)且仅终态(SUCCEEDED/SKIPPED/DEAD)可归档;保留期可配置为 1~7 天;
**定案口径(ACM2-17**:严禁向共享 MySQL 写入 `CMINMSGS_HST`(共享库严格保持两表 DML 契约);
归档目标为自有 PG `PROC_STATE_HST`(及 `MSG_EVENT_HST`)。非终态(PENDING/FAILED)禁止归档。
- PROJECTION_REBUILD(阶段 B 缓做,ACM2-12;重新评估后再启用)。
## 4. 失败与重试统一设计(U08)
@@ -235,8 +249,8 @@ ACM2-12)、`flyway.datasources.default.*`、`mailbox.shared-mysql.*`(共享
**范围注**:以上覆盖的是边界级(processOne/flushSchd/仓储)语义;主泵 tick 级 HOL/毒丸/退避
门禁因 Pump 未注入 Clock 而无单测锁定(§3.2 注),DispatcherTickTest 仅锁批退避与「队首未到期
不推进」。
- 现状 37 测试全绿(`./gradlew test`wrapper 钉 9.6.1 + JDK 25;内网构建设
`GRADLE_USER_HOME`/`TMPDIR` 指向可写目录)。
- 最近生成的 JUnit XML 报告为 39 项测试全绿;本轮文档审查因沙箱不能写用户级 Gradle 缓存,
未重新执行(wrapper 钉 9.6.1 + JDK 25;受限环境需将 `GRADLE_USER_HOME`/`TMPDIR` 指向可写目录)。
- **U29 门禁(规划)**:不变量清单化入 CI 红即阻塞;「新增逻辑必伴生不变量测试」入贡献约定。
## 8. 可观测性设计(U12 已落地部分)
@@ -252,7 +266,7 @@ ACM2-12)、`flyway.datasources.default.*`、`mailbox.shared-mysql.*`(共享
| 项 | 缺口 | 计划 |
|---|---|---|
| U05 | 自有 PG 数据层无实装 + **InboxPollerJDBC 轮询/重扫)** 未实装(当前仅 compat HTTP `InboxService.accept`);信箱适配层 CminmsgMailbox/OutboxMailbox`@Transactional`/allopen 未引入 → 生产 DI 装配失败;`application-dev.yml` 仍残留 MySQL datasource URL 占位(stub 下 enabled=false 不建连,U05 清理) | U05 批次(Micronaut Data JDBC on PG + InboxPoller + allopen + 信箱外部副作用与补偿回归) |
| U05 | JDBC PG 仓储、CMINMSGS 适配器与 **InboxPoller** 已有初版;但实现仍是逐操作独立连接,`MSG_EVENT + SUCCEEDED` 无本地事务,回填仍同步夹在两者之间;OutboxMailbox、可靠补偿、ARCHIVE 目标与真实双库集成测试未完成。`JdbcRefDataRepository` 仍为进程内过渡态,`JdbcFlightStateRepository` 为阶段 B 空实现 | 完成本地事务边界、回填/入队补偿、COUTMSGS 适配器与 Testcontainers 双库验收;生产启用前 fail-fast |
| U07/U26 | `autostart` 默认关=有意门禁,但生产无 fail-fast;双实例无运行期防护 | fail-fast 定案 + 租约/DB 锁拒启 |
| U09 | gen→Redis 协议未重设计(Lua 内原子版本推进;崩溃窗口=「Lua 完成/PG SUCCEEDED 未写」) | Redis 内版本 CAS + 恢复协议 + 测试(ACM2-12 |
| U13 | 投递侧无 createdAt/headDeadline 超时升级、无 DEAD 告警出口;处理侧 head-deadline 判据亦不可达(见 §3.2 注) | WP2 |
@@ -266,3 +280,9 @@ ACM2-12)、`flyway.datasources.default.*`、`mailbox.shared-mysql.*`(共享
| U27 | 专有材料(SIS md 703KB / XSD 版权头)治理决策 | WP4(ACL 核验先行) |
| ACM2-12 | 存储边界(自有 PG + 共享信箱 + Redis 动态/gen + 阶段 B 缓做):迁移 SQL/配置/接口注释已按定案调整(V1.0.0 PG);信箱适配层、gen Lua、作业窗口语义、影子重设计未实装 | ACM2-12 Checks ①–⑥ |
| ACM2-11 | **决策史(已被 ACM2-12 吸收)**:曾讨论 21 类静态独立 PG 参考库;定案为并入自有 PG `REF_MASTER`(见 ACM2-12),勿再按 `datasources.reference` 第二库规划 | 仅作决策脉络参考 |
## 10. 用户故事
面向需求优化的用户故事已集中到 [user-stories.md](user-stories.md)。该文档将目标能力、验收标准、
依赖与待确认问题分开,覆盖阶段 A US-01~US-14、延后 US-15、上线 EPIC 及 legacy HTTP 去留,
避免把当前实现、目标设计和遗留兼容行为混成同一项承诺。