在计划任务的管理系统中,我们遇到了一个关于任务重复计划的问题,特别是在任务队列繁忙时。具体来说,当任务执行函数 run() 在 jobs.create 返回 Busy 状态时,系统会执行以下操作:

UPDATE schedules SET next_run_at = now + BUSY_RETRY_MS ...

这条 SQL 语句会覆盖原本的 due 时间。当任务再次运行时,函数 following(due, ...) 会将这个被覆盖后的值作为网格的起点,导致原始的网格信息丢失。由于 request_key 包含了 due 信息,每次覆盖都会导致 request_key 发生变化,从而使得任务无法在同一个时间槽位进行重试。

问题重现

假设 first_run_at 设置为 10:00,interval 为 60 分钟。如果在 10:00 时任务队列已满,那么任务会在 10:01 执行,接着是 11:01 和 12:01。每次任务因为队列繁忙而无法执行时,都会累加偏移量。

修复方向

为了解决这个问题,我们可以考虑以下两种修复方案:

  1. 不覆盖 next_run_at,而是另存一个 retry_at 字段来记录下一次重试的时间;
  2. 在重试成功后,仍然使用原始的 due 来计算下一个执行槽位。

测试缺口

目前的测试用例文件 schedules_tests.rs 中缺少了处理 Busy 场景的测试用例,这可能会导致在开发过程中未能及时发现并修复这个问题。因此,我们需要补充相应的测试用例来确保系统的健壮性。