2026-09-09 17:53:08 +08:00
|
|
|
package com.gzzn.omms.msgexchange.jobs
|
|
|
|
|
|
2026-09-12 20:34:56 +08:00
|
|
|
import com.gzzn.omms.msgexchange.config.OperationDayProps
|
2026-09-10 10:59:58 +08:00
|
|
|
import com.gzzn.omms.msgexchange.processing.BackfillService
|
2026-09-09 17:53:08 +08:00
|
|
|
import jakarta.inject.Singleton
|
2026-09-11 08:08:35 +08:00
|
|
|
import java.time.Clock
|
2026-09-09 17:53:08 +08:00
|
|
|
import java.time.Duration
|
|
|
|
|
import java.time.Instant
|
|
|
|
|
import java.time.LocalDate
|
|
|
|
|
import java.time.ZoneId
|
|
|
|
|
|
|
|
|
|
/**
|
2026-09-10 11:05:55 +08:00
|
|
|
* 维护作业线程:定时做那些不需要跟消息一起排队的事。
|
|
|
|
|
*
|
|
|
|
|
* - 每 30 秒扫一次还欠回填的记录,把处理标记补写回共享信箱;
|
|
|
|
|
* - 每天机场时间 3:30 之后跑一次航班历史归档与留痕清理。
|
|
|
|
|
*
|
|
|
|
|
* 作业跑在自己的线程上,不占用消息处理循环,也不会让到期的消息饿死在这里。
|
|
|
|
|
* 回填意图在处理完成时就已经写进数据库了,本线程只负责到期重试。
|
2026-09-09 17:53:08 +08:00
|
|
|
*/
|
|
|
|
|
@Singleton
|
|
|
|
|
class JobRunner(
|
2026-09-10 10:59:58 +08:00
|
|
|
private val backfill: BackfillService,
|
2026-09-09 17:53:08 +08:00
|
|
|
private val historySweep: HistorySweepJob,
|
2026-09-11 08:08:35 +08:00
|
|
|
private val clock: Clock,
|
2026-09-12 20:34:56 +08:00
|
|
|
operationDayProps: OperationDayProps,
|
2026-09-09 17:53:08 +08:00
|
|
|
) {
|
|
|
|
|
private val log = org.slf4j.LoggerFactory.getLogger(JobRunner::class.java)
|
2026-09-12 20:34:56 +08:00
|
|
|
private val zone: ZoneId = operationDayProps.zoneId()
|
2026-09-09 17:53:08 +08:00
|
|
|
|
|
|
|
|
@Volatile
|
|
|
|
|
private var running = true
|
|
|
|
|
|
|
|
|
|
@Volatile
|
|
|
|
|
private var lastHistoryDay: LocalDate? = null
|
|
|
|
|
|
|
|
|
|
private var thread: Thread? = null
|
|
|
|
|
|
|
|
|
|
fun start() {
|
|
|
|
|
if (thread != null) return
|
|
|
|
|
running = true
|
|
|
|
|
thread = Thread.ofPlatform().name("msgx-jobs").daemon(true).start { loop() }
|
|
|
|
|
log.info("job runner started (backfill 30s, history daily 03:30 {})", zone)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun stop() {
|
|
|
|
|
running = false
|
|
|
|
|
thread?.interrupt()
|
|
|
|
|
thread = null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun loop() {
|
|
|
|
|
while (running) {
|
|
|
|
|
try {
|
2026-09-10 10:59:58 +08:00
|
|
|
backfill.sweep()
|
2026-09-09 17:53:08 +08:00
|
|
|
maybeHistorySweep()
|
|
|
|
|
} catch (e: InterruptedException) {
|
|
|
|
|
Thread.currentThread().interrupt()
|
|
|
|
|
return
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
log.warn("job tick failed: {}", e.message ?: e.javaClass.simpleName)
|
|
|
|
|
}
|
|
|
|
|
if (running) runCatching { Thread.sleep(Duration.ofSeconds(30).toMillis()) }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun maybeHistorySweep() {
|
2026-09-11 08:08:35 +08:00
|
|
|
// 时区换算后的机场时钟:切日与 03:30 门槛都以机场时区为准,且走注入 Clock(测试可确定)。
|
|
|
|
|
val airportClock = clock.withZone(zone)
|
|
|
|
|
val today = LocalDate.now(airportClock)
|
2026-09-09 17:53:08 +08:00
|
|
|
if (lastHistoryDay == today) return
|
2026-09-11 08:08:35 +08:00
|
|
|
val now = java.time.LocalTime.now(airportClock)
|
2026-09-09 17:53:08 +08:00
|
|
|
if ((now.hour == 3 && now.minute >= 30) || now.hour > 3) {
|
2026-09-11 08:08:35 +08:00
|
|
|
val outcome = historySweep.run(clock.instant())
|
2026-09-09 17:53:08 +08:00
|
|
|
lastHistoryDay = today
|
|
|
|
|
if (outcome.selected > 0 || outcome.snapLogPurged > 0) {
|
|
|
|
|
log.info("history sweep: {}", outcome)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|