提交更新
This commit is contained in:
+43
-35
@@ -569,42 +569,20 @@ public class AppearancePatentTaskService {
|
||||
}
|
||||
|
||||
public void finalizeStaleTasks() {
|
||||
if (transactionManager != null) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime threshold = now.minusMinutes(Math.max(1, properties.getStaleTimeoutMinutes()));
|
||||
long thresholdMillis = threshold.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
List<FileTaskEntity> tasks = listStaleFinalizeCandidates(threshold);
|
||||
for (FileTaskEntity task : tasks) {
|
||||
if (!isOwnerCurrent(ownerFromTask(task))) {
|
||||
continue;
|
||||
}
|
||||
long heartbeatMillis = taskCacheService.getTaskHeartbeatMillis(task.getId());
|
||||
if (heartbeatMillis > thresholdMillis) {
|
||||
continue;
|
||||
}
|
||||
TaskDistributedLockService.LockHandle lockHandle = acquireTaskLock(task.getId(), 0L);
|
||||
if (lockHandle == null) {
|
||||
continue;
|
||||
}
|
||||
try (lockHandle) {
|
||||
inNewTransaction(() -> {
|
||||
finalizeStaleTask(task.getId(), "Python interrupted before uploading final appearance patent result");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime threshold = now.minusMinutes(Math.max(1, properties.getStaleTimeoutMinutes()));
|
||||
long thresholdMillis = threshold.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
List<FileTaskEntity> tasks = listStaleFinalizeCandidates(threshold);
|
||||
// P1-7:stale 判定改用 Redis heartbeat 作为主信号。
|
||||
// 历史实现先按 file_task.updated_at 过滤再二次校验 Redis 心跳,
|
||||
// 但 file_task.updated_at 会被 Java 端在 "等 Python 上传" 分支自我续命
|
||||
// (resetStuckJobs 把 file_job 改回 PENDING → 重跑 processResultFileJob → touchJavaSideTaskActivity)。
|
||||
// Redis heartbeat 仅在 Python 心跳/上传 result 时写入,不会被 Java 内部循环刷新。
|
||||
long thresholdMillis = LocalDateTime.now()
|
||||
.minusMinutes(Math.max(1, properties.getStaleTimeoutMinutes()))
|
||||
.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
List<FileTaskEntity> tasks = listStaleFinalizeCandidates();
|
||||
for (FileTaskEntity task : tasks) {
|
||||
if (!isOwnerCurrent(ownerFromTask(task))) {
|
||||
continue;
|
||||
}
|
||||
long heartbeatMillis = taskCacheService.getTaskHeartbeatMillis(task.getId());
|
||||
if (heartbeatMillis > thresholdMillis) {
|
||||
if (!isHeartbeatStale(task, thresholdMillis)) {
|
||||
continue;
|
||||
}
|
||||
TaskDistributedLockService.LockHandle lockHandle = acquireTaskLock(task.getId(), 0L);
|
||||
@@ -612,7 +590,14 @@ public class AppearancePatentTaskService {
|
||||
continue;
|
||||
}
|
||||
try (lockHandle) {
|
||||
finalizeStaleTask(task.getId(), "Python interrupted before uploading final appearance patent result");
|
||||
if (transactionManager != null) {
|
||||
inNewTransaction(() -> {
|
||||
finalizeStaleTask(task.getId(), "Python interrupted before uploading final appearance patent result");
|
||||
return null;
|
||||
});
|
||||
} else {
|
||||
finalizeStaleTask(task.getId(), "Python interrupted before uploading final appearance patent result");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -643,15 +628,38 @@ public class AppearancePatentTaskService {
|
||||
}
|
||||
}
|
||||
|
||||
private List<FileTaskEntity> listStaleFinalizeCandidates(LocalDateTime threshold) {
|
||||
private List<FileTaskEntity> listStaleFinalizeCandidates() {
|
||||
// P1-7:不再用 file_task.updated_at 预过滤,由调用方按 Redis heartbeat 判 stale。
|
||||
// APPEARANCE_PATENT 在线 RUNNING 任务量级有限(限 200),全扫成本可忽略。
|
||||
return fileTaskMapper.selectList(new LambdaQueryWrapper<FileTaskEntity>()
|
||||
.eq(FileTaskEntity::getModuleType, MODULE_TYPE)
|
||||
.eq(FileTaskEntity::getStatus, STATUS_RUNNING)
|
||||
.lt(FileTaskEntity::getUpdatedAt, threshold)
|
||||
.orderByAsc(FileTaskEntity::getUpdatedAt)
|
||||
.last("limit 200"));
|
||||
}
|
||||
|
||||
/**
|
||||
* P1-7:判断 APPEARANCE_PATENT 任务是否已 stale。
|
||||
* 主信号:Redis heartbeat(仅 Python 心跳/上传 result 时刷新,不会被 Java 内部循环续命)。
|
||||
* Redis 命中:以 heartbeatMillis 与 thresholdMillis 比较。
|
||||
* Redis 缺失/降级(返回 0):回落到 file_task.updated_at,避免 Redis 故障时全量误杀。
|
||||
*/
|
||||
private boolean isHeartbeatStale(FileTaskEntity task, long thresholdMillis) {
|
||||
if (task == null || task.getId() == null) {
|
||||
return false;
|
||||
}
|
||||
long heartbeatMillis = taskCacheService.getTaskHeartbeatMillis(task.getId());
|
||||
if (heartbeatMillis > 0L) {
|
||||
return heartbeatMillis <= thresholdMillis;
|
||||
}
|
||||
LocalDateTime updatedAt = task.getUpdatedAt();
|
||||
if (updatedAt == null) {
|
||||
return false;
|
||||
}
|
||||
long updatedMillis = updatedAt.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
return updatedMillis <= thresholdMillis;
|
||||
}
|
||||
|
||||
private SubmitContext persistSubmittedChunk(Long taskId, AppearancePatentSubmitResultRequest request) {
|
||||
FileTaskEntity task = fileTaskMapper.selectById(taskId);
|
||||
if (task == null || !MODULE_TYPE.equals(task.getModuleType())) {
|
||||
|
||||
Reference in New Issue
Block a user