"""指数退避计算工具。 本模块提供两个函数: - ``calc_backoff_seconds``:根据连续错误数从退避序列取退避秒数。 - ``calc_backoff_next_run_at``:计算退避后的下次运行时间。 设计要点(见设计方案 §8.2): - 退避序列由配置 ``scheduler_backoff_schedule`` 提供(如 ``[10, 30, 120, 600, 3600]``)。 - 根据 ``consecutive_errors`` 从序列取值,超过序列长度时取最后一个值。 - ``consecutive_errors`` 为 0 时返回 0(无退避)。 - 时间计算基于 ``utc_now_naive()``,结果为 UTC naive datetime。 - 纯函数,不依赖框架层组件。 """ from __future__ import annotations from datetime import datetime, timedelta from yuxi.utils.datetime_utils import utc_now_naive def calc_backoff_seconds(consecutive_errors: int, schedule: list[int]) -> int: """根据连续错误数计算退避秒数。 Args: consecutive_errors: 连续错误次数。 schedule: 退避时间表(秒),如 ``[60, 300, 1800, 7200]``。 Returns: 退避秒数,超过 ``schedule`` 长度时取最后一个值。 说明: - ``consecutive_errors <= 0`` 时返回 0(无退避)。 - ``consecutive_errors`` 在 ``[1, len(schedule)]`` 范围内时,取 ``schedule[consecutive_errors - 1]``。 - ``consecutive_errors`` 超过序列长度时,取 ``schedule[-1]``(封顶)。 - ``schedule`` 为空时返回 0。 """ if consecutive_errors <= 0 or not schedule: return 0 index = min(consecutive_errors, len(schedule)) - 1 return schedule[index] def calc_backoff_next_run_at( consecutive_errors: int, schedule: list[int], *, base_time: datetime | None = None, ) -> datetime: """计算退避后的下次运行时间。 Args: consecutive_errors: 连续错误次数。 schedule: 退避时间表(秒)。 base_time: 基准时间(UTC naive),默认 ``utc_now_naive()``。 Returns: UTC naive datetime,退避后的下次运行时间。 说明: ``consecutive_errors`` 为 0 时退避秒数为 0,返回 ``base_time`` (或当前 UTC 时间)。 """ base = base_time if base_time is not None else utc_now_naive() seconds = calc_backoff_seconds(consecutive_errors, schedule) return base + timedelta(seconds=seconds)