分析与解决方案:backend/src/schedules.rs 的 run 函数中的 Err(AppError::Busy) 问题

问题所在

在 backend/src/schedules.rs 文件的 run 函数中,当遇到 Err(AppError::Busy) 错误时,目前的处理方式是将 next_run_at 设置为 now + BUSY_RETRY_MS,而不是保留原始的 due 值。这种做法导致后续的 following() 函数继续使用这个被修改过的值,从而造成每次执行任务时都存在相同的偏差。

行为描述

具体的行为表现如下:

  1. 新建一个计划任务,首次执行时间为 10:00,间隔设置为 60 分钟。
  2. 在 10:00 时,人为使队列达到满载状态,模拟繁忙状态。
  3. 当队列恢复后,计划任务试图在 10:01 执行。
  4. 由于 next_run_at 被错误地设置为 now + BUSY_RETRY_MS,后续的执行时间依次变为 11:01、12:01,而不是预期的 11:00、12:00。

附带问题

另一个问题是 request_key 中包含了 due 字段,而在重试时 key 发生了变化。这与注释中提到的“重试同一个槽位”相矛盾,因为键的变化意味着任务可能被分配到了不同的槽位。

解决方案与建议

为了解决这个问题,建议采取以下措施:

  1. 仅延迟本次触发:当遇到 Err(AppError::Busy) 错误时,应该只延迟本次任务的触发,而不是影响后续的所有执行计划。
  2. 单独保存重试时间:可以引入一个新的字段来单独保存重试的时间,这样即使任务因为繁忙而重试,也不会影响到原本的执行计划。
  3. 按原始 due 计算下一槽位:在任务成功执行后,应该根据原始的 due 值来计算下一个执行槽位,确保执行时间的准确性。

此外,建议补充针对 Busy 路径的测试,以确保上述修改能够正确地解决偏差问题,并验证 request_key 在重试时是否保持不变,以符合“重试同一个槽位”的设计要求。

测试建议

  • 测试在繁忙状态下创建的任务是否会正确地按照原始计划执行。
  • 验证在任务重试时,request_key 是否保持不变,确保任务仍然在同一个槽位执行。
  • 确认任务在成功执行后,后续的执行时间是否按照原始的 due 值来计算。

通过这些测试,可以确保修改后的代码能够满足需求,并解决当前存在的问题。