ActiveJob 的 polynomially_longer 重试时间如何计算?
使用 wait: :polynomially_longer 时,等待时间随执行次数的四次方增长,并默认加入随机抖动,因此没有完全固定的时间表。默认的 attempts: 5 包含首次执行,最多安排 4 次重试。关闭抖动后,这四次等待依次为 3、18、83、258 秒。
polynomially_longer 需要显式指定;retry_on 默认的 wait 是 3 秒。默认参数和尝试次数的含义见 Rails retry_on 文档。
等待时间如何计算
原公式为:
((executions**4) + (Kernel.rand * (executions**4) * jitter)) + 2
以下假设任务持续触发同一条 retry_on 规则,且每次执行都失败。令 n 表示当前失败的执行次数:首次执行失败时,n = 1,计算的是第二次执行前的等待时间。
默认 jitter 为 0.15。不带参数的 Kernel.rand 返回大于等于 0、小于 1 的值,因此等待时间范围是:
n**4 + 2 <= wait < 1.15 * n**4 + 2
单位为秒。随机增量只作用于 n**4,不包括最后加上的 2。设置 jitter: 0 后,等待时间固定为 n**4 + 2。
5、10、20 次尝试的时间表
表中的等待时间是距离上一次失败的间隔,不是从任务首次启动算起的累计时间。默认抖动一列采用左闭右开区间,即包含下限、不包含上限。
| 第几次重试 | 下一次是第几次执行 | jitter: 0,秒 | 默认 jitter: 0.15,秒 |
|---|---|---|---|
| 1 | 2 | 3 | [3, 3.15) |
| 2 | 3 | 18 | [18, 20.40) |
| 3 | 4 | 83 | [83, 95.15) |
| 4 | 5 | 258 | [258, 296.40) |
| 5 | 6 | 627 | [627, 720.75) |
| 6 | 7 | 1298 | [1298, 1492.40) |
| 7 | 8 | 2403 | [2403, 2763.15) |
| 8 | 9 | 4098 | [4098, 4712.40) |
| 9 | 10 | 6563 | [6563, 7547.15) |
| 10 | 11 | 10002 | [10002, 11502.00) |
| 11 | 12 | 14643 | [14643, 16839.15) |
| 12 | 13 | 20738 | [20738, 23848.40) |
| 13 | 14 | 28563 | [28563, 32847.15) |
| 14 | 15 | 38418 | [38418, 44180.40) |
| 15 | 16 | 50627 | [50627, 58220.75) |
| 16 | 17 | 65538 | [65538, 75368.40) |
| 17 | 18 | 83523 | [83523, 96051.15) |
| 18 | 19 | 104978 | [104978, 120724.40) |
| 19 | 20 | 130323 | [130323, 149871.15) |
对应关系如下:
attempts: 5:使用前 4 行。attempts: 10:使用前 9 行。attempts: 20:使用全部 19 行。
如果任务始终失败,将这些等待间隔相加,可以得到最后一次执行前的累计等待时间:
| attempts | 最多重试次数 | jitter: 0,累计秒数 | 默认 jitter: 0.15,累计秒数 |
|---|---|---|---|
| 5 | 4 | 362 | [362, 415.10) |
| 10 | 9 | 15351 | [15351, 17650.95) |
| 20 | 19 | 562704 | [562704, 647103.90) |
换算后,10 次尝试的累计等待约为 4.26 至 4.90 小时,20 次尝试约为 6.51 至 7.49 天。这些数值按公式计算,不包含任务执行耗时和队列调度延迟。
用 Ruby 生成其他次数的时间表
将 attempts 改为需要查看的总尝试次数即可。下面的代码计算等待范围,不执行任务,也不抽取随机数:
attempts = 20
jitter = 0.15
(1...attempts).each do |n|
minimum = n**4 + 2
upper_bound = minimum + n**4 * jitter
printf(
"Retry %2d -> execution %2d: %.2f <= wait < %.2f seconds\n",
n, n + 1, minimum, upper_bound
)
end
若需要固定间隔,可以在 retry_on 中显式设置 jitter: 0,再按 n**4 + 2 计算。
实际使用时要注意什么
attempts 限制的是这条 ActiveJob 重试规则的尝试次数。达到上限后,如果没有通过传入的代码块处理最终失败,异常会继续向底层队列系统传播。队列后端可能还有自己的重试机制,因此不能直接把表中的时间视为整个队列系统的最终执行时间表。Rails 文档说明了这一行为。
问题没有指定 Rails 版本,以上按所链接文档中的公式和默认参数计算。若项目修改了抖动配置,应使用实际生效的 jitter 重新计算。
备注:内容仅供参考。
