feat(processing): 实现自有 PostgreSQL 运营航班权威存储与单事务闭环 (ACM2-28)
- FS1: 增加 Flyway 迁移 V1.1.0__flight_schd.sql,创建 FLIGHT_SCHD 与 SCHD_GEN - FS2: 实现 FlightSchdRepository 接口及 JdbcFlightSchdRepository 与 StubFlightSchd,增强 JdbcOps 事务管理 - FS3: 扩展 MessageProcessor 事务 2 与按 FLID 点查视图,合并变更、事件与终态入单事务提交 - FS4: SnapshotFlow SQL 化(批处理 upsert、域内差删、SQL CAS 推进与熔断保护),JobExecutor 接入 PG 清场删除 - FS5: 彻底退役 Redis 权威与写路径,移除 FlightRedisClient、Lua 脚本、健康指示器与配置残留 - FS6: 补齐 U09/U29 不变量门禁(崩溃幂等、CAS 防并发、ADFT 存活保障、非 UTC JVM/会话时区无漂移)与 FlywayMigrationTest - FS7: 交付影子对拍比较内核 FlightStoreDiffTool 与单元测试 - FS8: 全面回改 decision-flight-state、architecture、design、user-stories 权威文档与规范
This commit is contained in:
@@ -11,7 +11,8 @@ import com.gzzn.omms.msgexchange.infra.persistence.CminmsgInboxRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.MsgEventRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.ProcStateRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.PumpJobRepository
|
||||
import com.gzzn.omms.msgexchange.infra.redis.FlightRedisClient
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.FlightSchdRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.PipelineTransactionManager
|
||||
import com.gzzn.omms.msgexchange.infra.retry.ProcFailure
|
||||
import com.gzzn.omms.msgexchange.jobs.JobExecutor
|
||||
import jakarta.inject.Singleton
|
||||
@@ -20,7 +21,7 @@ import java.time.Instant
|
||||
|
||||
/**
|
||||
* ACMA-8 流程 2:处理主泵——严格 FIFO + HOL + 毒丸 DEAD 升级(I1);
|
||||
* 阶段 A 全部 Redis 写在本线程(I5),且先于事件创建(I2 happens-before)。
|
||||
* 阶段 A 运营航班 FLIGHT_SCHD 与待发事件、终态同事务原子提交(I2/I5,ACM2-28 定案)。
|
||||
*/
|
||||
@Singleton
|
||||
class Pump(
|
||||
@@ -114,12 +115,25 @@ class MessageProcessor(
|
||||
private val msgEvents: MsgEventRepository,
|
||||
private val codecHolder: CodecHolder,
|
||||
private val handlers: HandlerHolder,
|
||||
private val redis: FlightRedisClient,
|
||||
private val flightSchd: FlightSchdRepository,
|
||||
private val snapshotFlow: SnapshotFlow,
|
||||
private val procFailure: ProcFailure,
|
||||
private val props: PipelineProps,
|
||||
private val txManager: PipelineTransactionManager,
|
||||
) {
|
||||
private val log = org.slf4j.LoggerFactory.getLogger(MessageProcessor::class.java)
|
||||
private val flidRegex = Regex("<FLID>(.*?)</FLID>", RegexOption.IGNORE_CASE)
|
||||
|
||||
private fun extractCandidateFlids(decoded: DecodedMessage): Set<String> {
|
||||
val fromXml = flidRegex.findAll(decoded.rawXml).map { it.groupValues[1].trim() }.filter { it.isNotEmpty() }.toSet()
|
||||
if (fromXml.isNotEmpty()) return fromXml
|
||||
val b = decoded.body
|
||||
if (b is Map<*, *>) {
|
||||
val flid = b["FLID"] ?: b["flid"]
|
||||
if (flid != null) return setOf(flid.toString())
|
||||
}
|
||||
return emptySet()
|
||||
}
|
||||
|
||||
fun processOne(head: ProcState) {
|
||||
com.gzzn.omms.msgexchange.infra.log.TraceLog.withTrace(head.cminmsgsId) {
|
||||
@@ -176,8 +190,7 @@ class MessageProcessor(
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 快照消息走专属流程(流程 4:staging→Lua→putGenIfVersion CAS 同事务)
|
||||
// 快照消息走专属流程(流程 4:staging → SQL 批处理/域内差删/CAS 同事务)
|
||||
val handler = handlers.registry.dispatcherFor(decoded)
|
||||
if (handler == null) {
|
||||
// U10(N21):未注册 ≠ 报文非法——写 FAILED(可重放),绝不写终态
|
||||
@@ -193,23 +206,35 @@ class MessageProcessor(
|
||||
return
|
||||
}
|
||||
|
||||
val decision = handler.decide(redis.hgetAllFlightInfo(), decoded) // 纯函数
|
||||
|
||||
if (props.phase == PipelineProps.Phase.A) {
|
||||
// 阶段 A:主泵线程先写 Redis(幂等),先于事件创建(I2);TODO: redisApply(flightChanges)
|
||||
val candidateFlids = extractCandidateFlids(decoded)
|
||||
val flightView = if (candidateFlids.isNotEmpty()) {
|
||||
flightSchd.findByFlids(candidateFlids)
|
||||
} else {
|
||||
emptyMap()
|
||||
}
|
||||
// 事务 2(自有 PG 内原子):事件 + SUCCEEDED(实装后 @Transactional);
|
||||
// CMINMSGS 回填 = 共享信箱外部副作用(最终一致,ACM2-12);
|
||||
// 静态主数据(refUpserts)同自有 PG 但弱事务独立提交(21 类 REF_MASTER)。
|
||||
|
||||
val decision = handler.decide(flightView, decoded) // 纯函数
|
||||
|
||||
// 事务 2(自有 PG 单事务原子):
|
||||
// upsert FLIGHT_SCHD(decision.flightChanges) + insert MSG_EVENT + PROC_STATE → SUCCEEDED
|
||||
val events = buildList {
|
||||
decision.msgNotifies.forEach { add(MsgEvent(target = Targets.KAFKA_MSG, payloadJson = it.payloadJson)) }
|
||||
decision.schdPush.forEach { add(MsgEvent(target = Targets.KAFKA_SCHD, partitionKey = it.flid, payloadJson = it.fltrJson)) }
|
||||
}
|
||||
msgEvents.insertAll(events)
|
||||
|
||||
txManager.inTransaction {
|
||||
if (decision.flightChanges.isNotEmpty()) {
|
||||
flightSchd.upsertIncremental(decision.flightChanges)
|
||||
}
|
||||
if (events.isNotEmpty()) {
|
||||
msgEvents.insertAll(events)
|
||||
}
|
||||
procState.update(head.cminmsgsId, ProcStatus.SUCCEEDED)
|
||||
}
|
||||
|
||||
// 提交后:backfill CMINMSGS(共享信箱外部副作用,补偿链路)
|
||||
inbox.backfillOnSuccess(head.cminmsgsId, decoded.meta.sndr, decoded.meta.type, decoded.meta.styp, decoded.meta.seqn)
|
||||
procState.update(head.cminmsgsId, ProcStatus.SUCCEEDED)
|
||||
log.info("SUCCEEDED id={} events={}", head.cminmsgsId, events.size)
|
||||
// 阶段 B:flightState.apply(decision.flightChanges) 进入同一事务;投影事件(ES/REDIS)追加。
|
||||
log.info("SUCCEEDED id={} events={} flightChanges={}", head.cminmsgsId, events.size, decision.flightChanges.size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user