在处理任务调度时,我们可能会遇到一个常见的问题,即任务在尝试执行时由于资源繁忙而无法立即运行。这种情况在任务调度系统中很常见,特别是在高负载环境下。本文将探讨这个问题,并提供可能的解决方案。

问题背景

在任务调度系统中,每个任务都有一个预定的执行时间,通常由字段 due 表示。当任务调度器尝试执行任务时,如果发现资源繁忙(即返回 Busy 状态),系统通常会等待一段时间后再次尝试执行任务。这个等待时间通常由 BUSY_RETRY_MS 定义。然而,在当前的设计中,当任务因资源繁忙而无法执行时,系统会更新 next_run_at 字段,这可能会覆盖原始的 due 值。

问题影响

当 next_run_at 被覆盖后,再次运行任务时,系统可能会使用被覆盖后的值作为网格起点,导致原始的执行计划丢失。此外,由于 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:在这种情况下,我们可以引入一个新的字段 retry_at 来存储重试时间,而不是覆盖 next_run_at。这样可以确保原始的 due 值不会被覆盖,从而保持原始的执行计划。
  2. 重试成功后仍用原始 due 计算下一个槽位:另一种解决方案是在任务重试成功后,仍然使用原始的 due 值来计算下一个执行槽位。这样可以确保任务按照原始计划执行,避免因资源繁忙而导致的执行计划混乱。

测试缺口

目前,在 schedules_tests.rs 测试用例中,没有包含资源繁忙的场景。为了确保修复方案的有效性,我们需要添加相应的测试用例来覆盖这种情况。

结论

通过引入新的字段或调整现有的执行逻辑,我们可以有效地解决任务调度中因资源繁忙而导致的执行计划混乱问题。通过添加相应的测试用例,我们可以确保修复方案的可靠性和稳定性。