refactor(flight-state): 按 flight-state.md 审计定稿全量重构脚手架与 SQL (ACM2-31)
- SQL 基线 V1__flight_state_baseline.sql 整体取代 V1.0.0–V1.4.0: PIPELINE_LOCK/PROC_STATE/MSG_EVENT/REQ_TRACK/BACKFILL_TODO/FLIGHT_SCHD + 8 张资源明细表 + FLIGHT_ROUTE_POINT + SCHD_SNAP_LOG 留痕层 - 废除 FDAY 日代/SCHD_GEN/名单差删:OPERATION_DAY 不可变(应用层校验 + 条件更新强化 §7.4),STATE 仅 ACTIVE/DELETED,物理清除只在历史归档后 - 处理器化:applyScheduleRecords(§5.1 七步同一事务,重放判定/整包 DEAD(PROTOCOL)/归属冲突不落地)+ FLOP/FDEL/ADFT(tombstone 仅 ACTIVE→DELETED,重复 FDEL 幂等不推进版本) - 投递:KAFKA_SCHD 同 FLID 按最新 STATE_VERSION 合并,被压掉事件关闭, TOMBSTONE 发 null 值消息(键缺失=删除旧值 §7.3) - 回填待办改为业务事务内预登记,消除提交后写待办的崩溃窗口(§7.2/§10) - XML 解码改为 jackson-dataformat-xml 数据类直接映射(SIS 信封强类型, FLTR 开放标签泛型承载) - 历史归档/物理清除顺序不可颠倒:归档确认成功集才物理删除,未接通删 0 条 - 移除 PUMP_JOB 队列/ReferenceService/FlightStoreDiffTool 等旧机制与测试, 新增运营日/引擎/快照/FDEL/归档顺序不变性回归测试
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
package com.gzzn.omms.msgexchange.jobs
|
||||
|
||||
import com.gzzn.omms.msgexchange.config.HistoryProps
|
||||
import com.gzzn.omms.msgexchange.config.PipelineProps
|
||||
import jakarta.inject.Singleton
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneId
|
||||
|
||||
/**
|
||||
* 维护作业调度(docs/flight-state.md §8):单 daemon 线程,独立于主泵——
|
||||
* 旧「作业不插队 PUMP_JOB 队列」机制随审计口径移除(§3.2 表清单无 PUMP_JOB);
|
||||
* 历史归档/留痕清理均为内部清理路径,不参与 FIFO 消息序。
|
||||
* 触发:回填补偿 30s 固定间隔;历史归档/留痕清理每日 03:30(机场时区)后首个 tick。
|
||||
*/
|
||||
@Singleton
|
||||
class JobRunner(
|
||||
private val backfillSweep: BackfillSweepJob,
|
||||
private val historySweep: HistorySweepJob,
|
||||
@Suppress("unused") private val pipelineProps: PipelineProps,
|
||||
@Suppress("unused") private val historyProps: HistoryProps,
|
||||
) {
|
||||
private val log = org.slf4j.LoggerFactory.getLogger(JobRunner::class.java)
|
||||
private val zone: ZoneId = ZoneId.of("Asia/Shanghai")
|
||||
|
||||
@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 {
|
||||
backfillSweep.sweep()
|
||||
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() {
|
||||
val today = LocalDate.now(zone)
|
||||
if (lastHistoryDay == today) return
|
||||
val now = java.time.LocalTime.now(zone)
|
||||
if ((now.hour == 3 && now.minute >= 30) || now.hour > 3) {
|
||||
val outcome = historySweep.run()
|
||||
lastHistoryDay = today
|
||||
if (outcome.selected > 0 || outcome.snapLogPurged > 0) {
|
||||
log.info("history sweep: {}", outcome)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user