diff --git a/docs/requirements.md b/docs/requirements.md index 4dcab6a0..085c927c 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -36,8 +36,8 @@ ## 可靠性 1. RSS 只重试传输错误,以及 408、425、429、500、502、503、504 HTTP 状态;其他 HTTP 错误立即失败。重试默认随机等待 3–5 秒,服务端返回有效 `Retry-After` 时至少等待其指定时长;重试次数可配置。 -2. 重试耗尽则记录警告日志并继续任务;RSS 是可选补充源,其获取失败不终止任务,但连续失败达到阈值时独立发出运维告警。连续失败和长期无更新告警均采用至少一次投递;投递或状态写入失败时记录日志并在后续任务中重试,不得使简报任务失败,接收端应按告警类型、源集合和失败周期去重。 -3. 任务发生非 RSS 错误时立即向运维通知渠道报警;投递失败时在后续任务失败中重试,成功投递后同一轮失败不重复告警,任务成功后重新开放。RSS 获取连续失败达到可配置阈值(默认 3)时独立报警,与任务失败隔离。任一已配置 RSS 源超过配置的时长(默认 24 小时)没有新文章也触发报警。未配置 RSS 不属于故障。 +2. 重试耗尽则记录警告日志并继续任务;RSS 是可选补充源,其获取失败不终止任务,但连续失败的调度轮次达到阈值时独立发出运维告警。一次调度轮次可在 source adapter 内包含多次 HTTP 尝试,但只给连续失败计数加一。连续失败和长期无更新告警均采用至少一次投递;投递或状态写入失败时记录日志并在后续任务中重试,不得使简报任务失败,接收端应按告警类型、源集合和失败周期去重。 +3. 任务发生非 RSS 错误时立即向运维通知渠道报警;投递失败时在后续任务失败中重试,成功投递后同一轮失败不重复告警,任务成功后重新开放。RSS 获取连续失败的调度轮次达到可配置阈值(默认 3)时独立报警,与任务失败隔离。任一已配置 RSS 源超过配置的时长(默认 24 小时)没有新文章也触发报警。未配置 RSS 不属于故障。 ## 架构与隐私 diff --git a/tests/test_service.py b/tests/test_service.py index 5b0df30e..6ac084c4 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -1545,7 +1545,7 @@ async def test_rss_failure_alert_is_sent_after_threshold( # Second failure: alert should trigger await service.run("briefing", now.add(hours=1)) assert len(ops_publisher.messages) == 1 - assert "持续获取失败" in ops_publisher.messages[0][0].body + assert "已连续至少 2 个调度轮次获取失败" in ops_publisher.messages[0][0].body # Third failure: no new alert (already alerted) await service.run("briefing", now.add(hours=2)) diff --git a/weather_briefing/service.py b/weather_briefing/service.py index e37cf572..ff5f3986 100644 --- a/weather_briefing/service.py +++ b/weather_briefing/service.py @@ -203,7 +203,8 @@ async def _run( if rss_failure_alert_ids: await self._publish_rss_health_alert( "天气 RSS 源持续获取失败", - f"以下 RSS 源已连续失败 {self._settings.rss_failure_threshold} 次:{', '.join(rss_failure_alert_ids)}", + f"以下 RSS 源已连续至少 {self._settings.rss_failure_threshold} 个调度轮次获取失败:" + f"{', '.join(rss_failure_alert_ids)}", lambda: self._state.mark_rss_failure_alerted(tuple(rss_failure_alert_ids), now), ) stale = self._state.stale_sources_requiring_alert(