docs(kdoc): 重写信箱生命周期相关 KDoc,改为直白说明并去掉内部编号

原有注释大量引用 §5.1/§5.2/Q2/US-01 这类文档编号和内部简称,跳过了"这段代码在做什么、
为什么这么做",没有读过设计文档的人基本读不懂。本次统一改成先讲清这件事本身、
再说为什么要这样做,编号只在末尾留一处指路。

覆盖本次改动涉及的 24 个 Kotlin 文件(生产 16 个 + 测试 8 个):

- 领域与端口:ProcState(补齐全量字段说明与状态/错误分类逐项注释)、
  ProcStateRepository / InboxCursorRepository / CminmsgInboxRepository 及
  MailboxRow / BackfillDue / Backlog;
- 收报:InboxPoller(把"水位连续、遇缺口停下、缺口老化"用大白话讲透)、
  InboxService、JdbcCminmsgInboxRepository;
- 处理:Pump / MessageProcessor、ScheduleProcessor、DynamicProcessors、
  ProcFailure、JdbcProcStateRepository 与游标实现;
- 回填与观测:BackfillService、InboxLifecycleHealthIndicator、JobRunner;
- 配置与 stub:PipelineProps(三个新增参数说清取值理由)、MailboxProps、
  StubRepositories;
- 测试:8 个测试类改为"这些用例在守哪几条规矩",并保留 H2 不覆盖
  ON CONFLICT 的说明。

术语统一按第一次出现就地解释:水位、处理标记、回填、死信、队头、终态。
纯注释改动;除拆分枚举时按仓库风格补的两个行尾逗号外无代码变更
(已用剥离注释后比对 HEAD 的方式逐文件核对)。测试仍为 78 passed / 1 skipped。
This commit is contained in:
windyboy
2026-09-10 11:05:55 +08:00
parent 8c6bb83b62
commit d475feb790
24 changed files with 332 additions and 186 deletions
@@ -19,11 +19,16 @@ import java.time.Duration
import java.time.Instant
/**
* 处理主泵docs/flight-state.md §1 严格有序 + §4 处理事务与失败规则):
* 单活动主泵严格 FIFO + HOL 阻塞 + 队头滞留转 DEAD。
* 处理主泵:一个线程按消息 ID 从小到大一条条处理,保证先来的先处理。
*
* 终态与业务写入在处理器事务内原子提交;本泵只负责调度、边界化失败迁移与
* 提交后的最佳努力回填(message-lifecycle §3/§4)。
* 每次 tick 只看当前最小的未完成消息("队头"):
* - 没有待处理消息就睡一个轮询间隔;
* - 队头失败了还在退避期,就等到能重试的时刻;如果重试次数用尽或滞留太久,
* 直接转死信,不放任它一直堵着;
* - 其余情况交给 [MessageProcessor] 处理。
*
* 一次只处理一条是刻意的。后面的消息不能越过卡住的队头,否则同一条航班的报文
* 可能被乱序应用,几十秒后才到的旧报文会把新状态覆盖回去。
*/
@Singleton
class Pump(
@@ -38,7 +43,7 @@ class Pump(
@Volatile
private var running = true
/** 优雅停机:loop 在当前 tick 收尾后退出线程中断由 PipelineLifecycle 负责。 */
/** 请求停机:当前 tick 跑完就退出线程中断由 PipelineLifecycle 负责。 */
fun stop() {
running = false
}
@@ -51,7 +56,7 @@ class Pump(
Thread.currentThread().interrupt()
return
} catch (e: Exception) {
// 最后防线:失败状态迁移已在 processOne 边界内完成;致命 Error 不捕获
// 兜底:单条消息的失败状态已在 processOne 内记录,这里只避免线程退出
sleepQuietly(props.pipeline.pollInterval)
}
}
@@ -74,7 +79,7 @@ class Pump(
} else {
sleepQuietly(Duration.between(clock.instant(), head.nextAttemptAt))
}
// PENDING、或 FAILED 退避到期:交处理入口(内部有边界化失败迁移与 attempts 守卫)
// 其余情况(新消息,或退避到期的重试)交给处理入口
else -> processor.processOne(head)
}
}
@@ -89,9 +94,13 @@ class Pump(
}
/**
* processOne:解码 → 绑定 → 处理器(事务内决策 + 落库 + 终态 + 回填意图)→ 提交后最佳努力回填
* 边界化失败迁移(ProcFailure):任何意外异常归于本条 headFAILED(INFRA)+退避,不穿出杀泵;
* MALFORMED / PROTOCOL 直接 DEAD 不重试(docs/design.md §2.3 错误分类)。
* 处理一条消息:读原文 → 解码 → 绑定业务身份 → 分派给对应处理器 → 提交后回填标记
*
* 业务数据和终态由各处理器在自己的事务里写入。终态一旦落下(成功、跳过或死信),
* 这里马上试一次把处理标记写回信箱;写不进去也没关系,回填扫描会按退避继续重试。
*
* 任何意外异常都算在当前这条消息头上(记 FAILED(INFRA) 后重试),不会把主泵线程带崩。
* 报文非法和整包协议拒绝不重试,直接进死信等人工处置。
*/
@Singleton
class MessageProcessor(
@@ -126,7 +135,7 @@ class MessageProcessor(
}
}
/** @return 是否已达终态(终态才允许回填信箱标记) */
/** @return 这条消息是否已经落到终态(只有终态才允许回填信箱标记) */
private fun processInternal(head: ProcState): Boolean {
// 守卫:手工/遗留 FAILED 行若 attempts 已达上限,直接终态(防止退避到期后无限重试)
if (head.state == ProcStatus.FAILED && procFailure.scheduler.exhausted(head.attempts)) {