docs: 二轮审查文档收敛 ACM2-23~27(Kafka/依赖/ResponseDto/STATUS/格式)
This commit is contained in:
+40
-20
@@ -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-10(U01–U30)**。文中标注「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) →
|
||||
decode(MALFORMED→DEAD / CODEC_ERROR→FAILED)→ identity 首绑
|
||||
(`tryBindIdentity` 失败 → SKIPPED,I3)→ Schd DNLD → `SnapshotFlow` →
|
||||
decode(MALFORMED→DEAD / CODEC_ERROR→FAILED)→ ignoreMsg 匹配(LDM/REGN/RSTA/EROR,
|
||||
命中→SKIPPED,回填遵循 US-09;当前未实装)→ identity 首绑
|
||||
(`tryBindIdentity` 失败 → SKIPPED,I3)→ Schd RESP/DNLD → `SnapshotFlow`(ACM2-16 定案:
|
||||
DNLD 与 RESP 均走 SnapshotFlow;RESP 成功后在同事务完成匹配开放 RQFD 的 `REQ_TRACK→DONE`;
|
||||
迟到或无匹配 RESP 严禁更新快照,直接转 SKIPPED 并审计)→
|
||||
其余 → `Handler.decide(redis.hgetAllFlightInfo(), msg)` →
|
||||
阶段 A:Redis 先写(I2 happens-before,TODO 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/D2,ACM2-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,检查开放 RQFD(dttm < sentAt 或无匹配/已过期 → 严禁更新快照,转 SKIPPED 并审计)
|
||||
→ Redis Lua SNAPSHOT_REPLACE(同一 hash 原子「覆盖新代+按代差删」,删除集=旧代flids−新代)
|
||||
→ gen 版本推进(ACM2-12:gen 随 flightInfo 同在 Redis,Lua 内原子版本 CAS——
|
||||
目标实现;现 RefDataRepository/putGenIfVersion 为过渡占位)
|
||||
→ 自有 PG SUCCEEDED
|
||||
```
|
||||
→ gen 版本推进(ACM2-12:gen 随 flightInfo 同在 Redis,Lua 内原子版本 CAS)
|
||||
→ 自有 PG 本地事务(原子性):PROC_STATE→SUCCEEDED + (RESP 匹配时)REQ_TRACK→DONE + MSG_EVENT 插入
|
||||
→ PG 提交后异步触发信箱回填(持久化补偿,ACM2-19)
|
||||
|
||||
数据流说明:SCHD-RESP 处理依赖 US-08 已登记的开放 REQ_TRACK;US-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_SWEEP(3:30 清场,I4 同步链):判史 → 同步写 ES → 仅删成功集。
|
||||
**占位门禁(U10/T07 修订)**:ES saveSync 接线前 `pickHistory` 恒空集、删除量恒 0,
|
||||
禁止「全量可删」fail-open 默认;现役五条判史规则 golden 通过后才允许接线。
|
||||
- ARCHIVE(3:00):1 天前且仅终态(SUCCEEDED/SKIPPED/DEAD)迁 CMINMSGS_HST(共享库,
|
||||
外部副作用;TODO U05 批次)。
|
||||
**阶段归属**:按 ACM2-12 阶段 B 缓做口径标为 DEFERRED,不作为阶段 A 切流门禁;启用前
|
||||
重新确认“历史链路不变”与阶段 B 投影范围、ES/OpenSearch 产品边界。
|
||||
- ARCHIVE(3: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 数据层无实装 + **InboxPoller(JDBC 轮询/重扫)** 未实装(当前仅 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 去留,
|
||||
避免把当前实现、目标设计和遗留兼容行为混成同一项承诺。
|
||||
|
||||
Reference in New Issue
Block a user