This commit is contained in:
Marsway 2026-01-13 14:22:37 +08:00
parent ccb4a42ab7
commit 6b581bcf71
2 changed files with 23 additions and 3 deletions

View File

@ -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

View File

@ -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 项目定位