2026-09-07 15:11:33 +08:00
|
|
|
|
package com.gzzn.omms.msgexchange.ingress
|
|
|
|
|
|
|
|
|
|
|
|
import com.gzzn.omms.msgexchange.config.PipelineProps
|
|
|
|
|
|
import com.gzzn.omms.msgexchange.infra.persistence.CminmsgInboxRepository
|
2026-09-08 13:04:43 +08:00
|
|
|
|
import com.gzzn.omms.msgexchange.jobs.BackfillSweepJob
|
2026-09-07 15:11:33 +08:00
|
|
|
|
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,
|
2026-09-08 13:04:43 +08:00
|
|
|
|
private val backfillSweep: BackfillSweepJob,
|
2026-09-07 15:11:33 +08:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-08 13:04:43 +08:00
|
|
|
|
sweepBackfillTodos()
|
2026-09-07 15:11:33 +08:00
|
|
|
|
return enqueued
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-08 13:04:43 +08:00
|
|
|
|
/** v2 §5:每轮心跳顺带对账回填补偿待办(重启即恢复;空表只花一次索引 SELECT)。 */
|
|
|
|
|
|
private fun sweepBackfillTodos() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
val outcome = backfillSweep.sweep()
|
|
|
|
|
|
if (outcome.inspected > 0) {
|
|
|
|
|
|
log.info("backfill sweep inspected={} succeeded={} failed={}", outcome.inspected, outcome.succeeded, outcome.failed)
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
|
log.error("backfill sweep tick failed", e)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-07 15:11:33 +08:00
|
|
|
|
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()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|