feat(ingress): 实现 JDBC 信箱轮询、入站持久化与对拍测试 (U05)

This commit is contained in:
windyboy
2026-09-07 15:11:33 +08:00
parent dc68f1e1f8
commit c7b4b527ef
51 changed files with 1005 additions and 235 deletions
@@ -0,0 +1,65 @@
package com.gzzn.omms.msgexchange.ingress
import com.gzzn.omms.msgexchange.config.PipelineProps
import com.gzzn.omms.msgexchange.infra.persistence.CminmsgInboxRepository
import jakarta.inject.Singleton
/**
* ACMA-8 流程 1 · 主路径:JDBC 轮询共享 MySQL CMINMSGS`DATE_PROCESSED IS NULL`),
* 发现新信后入队自有 PG。与 legacy `MsgExchangeRunner.getNewMsgsAfterId(0L)` 同语义。
*/
@Singleton
class InboxPoller(
private val inbox: CminmsgInboxRepository,
private val enqueue: InboxEnqueue,
private val props: PipelineProps,
) {
private val log = org.slf4j.LoggerFactory.getLogger(InboxPoller::class.java)
@Volatile
private var running = false
/** legacy 现役:afterId=0,每轮扫全部未处理行;PROC_STATE 判重防重复入队。 */
fun pollOnce(): Int {
val batch = props.pipeline.claimBatch.coerceAtLeast(1)
val ids = inbox.pollUnprocessed(afterId = 0L, limit = batch)
var enqueued = 0
for (id in ids) {
if (enqueue.enqueue(id)) {
enqueued++
log.info("polled cminmsgsId={}", id)
}
}
return enqueued
}
fun loop() {
running = true
log.info("inbox poller loop started")
while (running) {
try {
pollOnce()
sleepQuietly(props.pipeline.pollInterval)
} catch (_: InterruptedException) {
Thread.currentThread().interrupt()
break
} catch (e: Exception) {
log.error("inbox poller tick failed", e)
sleepQuietly(props.pipeline.pollInterval)
}
}
log.info("inbox poller loop stopped")
}
fun stop() {
running = false
}
private fun sleepQuietly(d: java.time.Duration) {
try {
Thread.sleep(d.toMillis().coerceAtLeast(1))
} catch (_: InterruptedException) {
Thread.currentThread().interrupt()
}
}
}