diff --git a/app/tasks/dispatcher.py b/app/tasks/dispatcher.py index da8d6e0..912aa1d 100644 --- a/app/tasks/dispatcher.py +++ b/app/tasks/dispatcher.py @@ -45,8 +45,17 @@ def tick() -> dict[str, int]: last_min = _floor_to_minute(last.replace(tzinfo=tz)) else: last_min = _floor_to_minute(last.astimezone(tz)) - if last_min >= now_min: - continue + # 防御:若 last_run_at 被错误写成 UTC 等导致“在未来”,则忽略该值避免任务永久不触发 + if last_min > now_min: + logger.warning( + "job.last_run_at appears in the future (ignored) job_id=%s last_run_at=%s now=%s", + job.id, + last, + now, + ) + else: + if last_min >= now_min: + continue # croniter 默认按传入 datetime 计算,这里用 Asia/Shanghai base = now_min - timedelta(minutes=1) @@ -56,7 +65,8 @@ def tick() -> dict[str, int]: continue execute_job.delay(job_id=job.id) - crud.update_job_last_run_at(session, job.id, now_min.replace(tzinfo=None)) + # 写入时保留 tz 信息,避免在 PostgreSQL timestamptz 中被误当 UTC 导致“未来 last_run_at” + crud.update_job_last_run_at(session, job.id, now_min) triggered += 1 except Exception: # noqa: BLE001 diff --git a/docs/connecthub_功能与部署指南.md b/docs/connecthub_功能与部署指南.md index ae0a63f..9353d82 100644 --- a/docs/connecthub_功能与部署指南.md +++ b/docs/connecthub_功能与部署指南.md @@ -6,6 +6,16 @@ --- +### 定时任务不触发排查:last_run_at 时区导致“被认为在未来” + +如果你发现某个 Job 第一次手动触发/定时触发成功后,后续按 cron 到点却一直不再触发(`dispatcher.tick` 的日志里 `triggered: 0`),很可能是 `last_run_at` 的时区写入/解释不一致导致: + +- 调度器会用 `Asia/Shanghai` 解释并比较 `last_run_at`,若认为 `last_run_at >= 当前分钟` 就会跳过触发; +- 如果数据库把 `last_run_at` 存成了 `... +00:00`(UTC),而你期望按 `+08:00` 计算,就可能出现“last_run_at 在未来 8 小时”的错觉,导致任务被持续跳过。 + +解决: +- 新版本已修复:写入 `last_run_at` 时保留时区信息,并在发现 `last_run_at` 明显晚于当前分钟(未来时间)时自动忽略该值,避免任务永久不触发。 + ## 1. 项目概览 ### 1.1 项目定位