Files
crawler-plugin/backend-java/docs/stale-task-finalize-guidelines.md
2026-06-08 18:31:55 +08:00

4.4 KiB
Raw Permalink Blame History

Stale 任务收尾约束(公共心跳模块)

适用范围:所有使用 TaskHeartbeatService 的 file_task 模块 PRODUCT_RISK_RESOLVE / PRICE_TRACK / SHOP_MATCH / PATROL_DELETE / QUERY_ASIN / APPEARANCE_PATENT / SIMILAR_ASIN / DELETE_BRAND

历史故障2026-06-08

SIMILAR_ASIN taskId=13102 的 file_job 长期持有 RUNNING 等待 Python 凑齐 batchSize 行再发 Coze。Python 端 13:20 异常掉线,但任务到下午 16 点仍卡在 12% 不前进, 30 分钟兜底失效。

链路:

  1. resetStuckJobs 每 60 秒扫一次,把 RUNNING 超 30 分钟的 file_job 改回 PENDING。
  2. worker 立刻把它捞起来重跑 processResultFileJob
  3. 重跑分支命中 "等 Python 上传更多 row" → 调用 touchJavaSideTaskActivityfile_task.updated_at 刷成 now。
  4. finalizeStaleTaskslt(updated_at, threshold) 预过滤,于是这个任务 永远进不到候选集Redis 心跳判定那一步根本走不到。

→ Python 已断线 N 小时,任务仍在循环,永不收尾。

约束

1. stale-finalize 必须以 Redis 心跳为主信号

TaskCacheService.touchTaskHeartbeat 写的 Redis key 仅在 Python 真实活动 (心跳接口、上传 result chunk、提交 row时刷新。Java 内部循环不应触碰它。

stale 判定时应:

  • 以 Redis heartbeat 为主信号;
  • 仅在 Redis 不可用 / 缺失时回落到 file_task.updated_at
  • 不要再用 lt(file_task.updated_at, threshold) 做预过滤 (除非该模块 100% 不存在长期 RUNNING 等待循环)。

参考实现:SimilarAsinTaskService.finalizeStaleTasks / isHeartbeatStaleAppearancePatentTaskService 中 P1-7 注释段。

2. 不要在"等 Python"分支刷 file_task.updated_at

如果模块的 file_job 会持有 RUNNING 状态等待外部上传更多数据, 这条等待路径上不允许调 setUpdatedAt(now) 类方法

否则会触发 resetStuckJobs → 重跑 → 续命 → 再 reset 的死循环。

如必须刷 updated_at 推动其他逻辑,应迁移到独立字段(例如 file_job.updated_at、Redis pendingScope 字段),与 stale 判定信号解耦。

3. 新增/修改 stale-finalize 流程的 PR 检查清单

  • stale 判定的主信号是 Redis 心跳(不是 file_task.updated_at
  • 列出该模块所有刷 file_task.updated_at 的代码位置, 其中没有任何一处会被 processResultFileJob 在"等 Python"分支重复触发
  • 如果 file_job 会持有长 RUNNINGpull-style 工作流),明确写明 stuck-reset 重跑后的幂等行为(不会续命 updated_at
  • 给 finalize 路径补一个 debug 接口(参考 DebugTaskRecoveryController 绕过过滤直接对单 task 强制收尾,便于线上应急

当前各模块状态2026-06-08 复核)

模块 stale 入口 是否曾出现自我续命 是否需要改
SIMILAR_ASIN SimilarAsinTaskService.finalizeStaleTasks 是(已修) 已切 Redis-first
APPEARANCE_PATENT AppearancePatentTaskService.finalizeStaleTasks 是(已修) 已切 Redis-first
PRODUCT_RISK_RESOLVE DeleteBrandStaleTaskService.failStaleProductRiskResolveTasks 当前不需要
PRICE_TRACK DeleteBrandStaleTaskService.failStalePriceTrackTasks 当前不需要
SHOP_MATCH DeleteBrandStaleTaskService.failStaleShopMatchTasks 当前不需要
PATROL_DELETE DeleteBrandStaleTaskService.failStalePatrolDeleteTasks 当前不需要
QUERY_ASIN DeleteBrandStaleTaskService.failStaleQueryAsinTasks 当前不需要
DELETE_BRAND DeleteBrandStaleTaskService.failStaleDeleteBrandTasks 当前不需要
BRAND BrandTaskService.failStaleRunningTasks(独立心跳源) 当前不需要

下次新增"等 row 凑批" / "pull-style 长 RUNNING file_job"型功能时, 请先读本约束。