feat(jobs): PROC_STATE 终态行到期清理(ACM2-82)
- 新增 deleteExpiredTerminal:仅删终态 + BACKFILL_AT 非空 + UPDATED_AT 超期的行(US-11) - ProcStateCleanupJob 每轮 tick 执行,与人工重放共用 MessageLifecycleGate,不持 PIPELINE_LOCK - 登记参数 msgx.pipeline.terminal-retention(默认 90d,暂定)进 reference.md - Stub 与 JDBC 实现同口径;补四类候选条件回归
This commit is contained in:
@@ -126,6 +126,13 @@ interface ProcStateRepository {
|
||||
/** 人工重放:把指定错误类别的 FAILED / DEAD 记录改回 PENDING,重试次数清零。 */
|
||||
fun requeueByErrorClasses(errorClasses: List<ErrorClass>): Int
|
||||
|
||||
/**
|
||||
* 到期清理(US-11):删除终态(SUCCEEDED/SKIPPED/DEAD)、已回填(BACKFILL_AT 非空)
|
||||
* 且 `UPDATED_AT < cutoff` 的 PROC_STATE 行;未完成或未回填的行一律不删。
|
||||
* @return 实际删除的行数
|
||||
*/
|
||||
fun deleteExpiredTerminal(cutoff: Instant, limit: Int): Int
|
||||
|
||||
/** 积压观测:还没处理完的条数、最老一条的接收时间、处理完但还没回填的条数。 */
|
||||
fun backlog(): Backlog
|
||||
|
||||
|
||||
+18
@@ -288,6 +288,24 @@ class JdbcProcStateRepository(
|
||||
)
|
||||
}
|
||||
|
||||
/** 到期清理(US-11):只删终态且已回填、UPDATED_AT 超期的行;条件带不进子查询的行不受影响。 */
|
||||
override fun deleteExpiredTerminal(cutoff: Instant, limit: Int): Int =
|
||||
ds.update(
|
||||
"""
|
||||
DELETE FROM proc_state WHERE msg_id IN (
|
||||
SELECT msg_id FROM proc_state
|
||||
WHERE state IN ('SUCCEEDED', 'SKIPPED', 'DEAD')
|
||||
AND backfill_at IS NOT NULL
|
||||
AND updated_at < ?
|
||||
ORDER BY msg_id LIMIT ?
|
||||
)
|
||||
""".trimIndent(),
|
||||
{ ps ->
|
||||
ps.setTimestamp(1, cutoff.toSqlTimestamp())
|
||||
ps.setInt(2, limit)
|
||||
},
|
||||
)
|
||||
|
||||
/** 积压观测:未处理条数、最老一条的接收时间、处理完但未回填的条数。 */
|
||||
override fun hasAny(): Boolean =
|
||||
ds.queryOne("SELECT 1 FROM proc_state LIMIT 1", {}) { 1 } != null
|
||||
|
||||
@@ -225,6 +225,19 @@ class StubProcState(private val clock: Clock = Clock.systemUTC()) : ProcStateRep
|
||||
return n
|
||||
}
|
||||
|
||||
/** 到期清理(US-11):仅删终态且已回填、UPDATED_AT 超期的行。 */
|
||||
override fun deleteExpiredTerminal(cutoff: Instant, limit: Int): Int {
|
||||
val due = rows.values
|
||||
.filter {
|
||||
it.state in TERMINAL_STATES && it.backfillAt != null && it.updatedAt.isBefore(cutoff)
|
||||
}
|
||||
.sortedBy { it.msgId }
|
||||
.take(limit)
|
||||
.map { it.msgId }
|
||||
due.forEach { rows.remove(it) }
|
||||
return due.size
|
||||
}
|
||||
|
||||
fun snapshotOf(msgId: Long): ProcState? = rows[msgId]
|
||||
}
|
||||
|
||||
@@ -549,3 +562,7 @@ class StubInboxCursor : InboxCursorRepository {
|
||||
/** 判断是不是终态:处理已经结束、不会再重试的状态。 */
|
||||
private fun ProcStatus.isTerminal(): Boolean =
|
||||
this == ProcStatus.SUCCEEDED || this == ProcStatus.SKIPPED || this == ProcStatus.DEAD
|
||||
|
||||
/** 终态集合:US-11 到期清理与回填扫描共用的口径。 */
|
||||
private val TERMINAL_STATES: Set<ProcStatus> =
|
||||
setOf(ProcStatus.SUCCEEDED, ProcStatus.SKIPPED, ProcStatus.DEAD)
|
||||
|
||||
Reference in New Issue
Block a user