ref(crons): Reorganize incident creation / issue occurrence logic - #1
ref(crons): Reorganize incident creation / issue occurrence logic#1linxia0415 wants to merge 1 commit into
Conversation
Since we'll be doing more with issue occurrences split out the concept of incidents into it's own logic module, as well as incident_occurrence into it's own module
There was a problem hiding this comment.
AI代码审查报告
变更概览
本次 PR 涉及 4 个文件,新增 +289 行,删除 -264 行。
功能变更摘要
本 PR 对监控器失败处理逻辑进行重构,将原有 mark_failed 模块的功能拆分为独立的事故管理模块,新增事故处理和事故发生逻辑,并补充相关类型定义以提升代码可维护性。
文件变更摘要
| 文件 | 变更 | 行数 | 摘要 | 发现问题 |
|---|---|---|---|---|
src/sentry/monitors/logic/incident_occurrence.py |
新增 | +171/-0 | 提取事故发生处理逻辑,独立成模块 | 3 个 |
src/sentry/monitors/logic/incidents.py |
新增 | +104/-0 | 新增事故管理核心逻辑 | 2 个 |
src/sentry/monitors/logic/mark_failed.py |
修改 | +4/-264 | 精简代码,将核心功能抽离至新模块 | — |
src/sentry/monitors/types.py |
修改 | +10/-0 | 补充监控器事故相关的类型定义 | — |
问题严重级别分布
| 级别 | 数量 | 占比 |
|---|---|---|
| 🟡 中危 | 5 | 100% |
代表性问题(至多 10 条,按严重级别优先)
- 🟡 中危
src/sentry/monitors/logic/incident_occurrence.pyL66: 潜在空指针解引用
line 66 处直接调用 monitor_env.get_environment().name,未检查 get_environment() 是否可能返回 None。line 89…
2. 🟡 中危 src/sentry/monitors/logic/incident_occurrence.py L67: 当环境记录异常或不存在时,监控事故处理流程将中断,可能导致监控状态无法正确更新。
3. 🟡 中危 src/sentry/monitors/logic/incident_occurrence.py L159: 监控环境上下文中的 schedule_type 将始终显示原始值而非人类可读的显示值,影响监控信息的可读性和调试。
4. 🟡 中危 src/sentry/monitors/logic/incidents.py L48: 在监控器历史数据不足的情况下,可能导致过早或错误地触发 incident,产生误报,影响运维人员对真实故障的判断。
5. 🟡 中危 src/sentry/monitors/logic/incidents.py L59: 对于新创建的监控器或历史数据被清理的监控器,当发生失败时会导致事故处理流程崩溃,无法创建 incident 记录,影响监控系统的可靠性。
由 CodeHawk 提供支持 · nuwa
| ), | ||
| IssueEvidence( | ||
| name="Environment", | ||
| value=monitor_env.get_environment().name, |
There was a problem hiding this comment.
🟡 AI 代码审查发现问题
📋 问题概述
发现 2 个邻近问题(第 66–67 行)
📍 问题详情
🟡 问题 1 | 严重程度: MEDIUM | 行号: 66
💬 详细说明:
- 潜在空指针解引用
- line 66 处直接调用 monitor_env.get_environment().name,未检查 get_environment() 是否可能返回 None。line 89 处存在相同模式。当数据库中 monitor_env 的 environment 外键为空或数据损坏时,get_environment() 返回 None,访问 .name 将抛出 AttributeError。
📝 问题代码:
value=monitor_env.get_environment().name,
💡 修复建议:
修复策略:添加 None 检查,在 get_environment() 返回 None 时使用默认值或跳过该字段。
操作步骤:将 line 66 改为 value=monitor_env.get_environment().name if monitor_env.get_environment() else "unknown",并对 line 89 做相同处理;或提前获取 environment 变量并统一检查。
✅ 修复示例:
env = monitor_env.get_environment()
occurrence = IssueOccurrence(
...
evidence_display=[
IssueEvidence(
name="Failure reason",
value=str(get_failure_reason(failed_checkins)),
important=True,
),
IssueEvidence(
name="Environment",
value=env.name if env else "unknown",
important=False,
),
...
],
...
)
event_data = {
...
"environment": env.name if env else "unknown",
...
}
🟡 问题 2 | 严重程度: MEDIUM | 行号: 67
💬 详细说明:
- 当环境记录异常或不存在时,监控事故处理流程将中断,可能导致监控状态无法正确更新。
📝 问题代码:
environment=monitor_env.get_environment().name,
💡 修复建议:
在访问 name 属性前,先检查 get_environment() 的返回值。可以使用安全访问模式或添加异常处理。
✅ 修复示例:
env = monitor_env.get_environment()
environment=env.name if env else "unknown",
🔗 参考链接
无
| return _("%(problem_checkins)s check-ins detected") % {"problem_checkins": human_status} | ||
|
|
||
|
|
||
| def get_monitor_environment_context(monitor_environment: MonitorEnvironment): |
There was a problem hiding this comment.
🟡 AI 代码审查发现问题
📋 问题概述
逻辑错误:config 副本修改无效。line 159-164 处创建了 monitor.config 的副本并修改 schedule_type 为可读字符串,但 line 171 返回时使用了原始的 monitor_environment.monitor.config,导致修改完全无效。
📍 问题详情
🟡 问题 1 | 严重程度: MEDIUM | 行号: 159
💬 详细说明:
- 监控环境上下文中的 schedule_type 将始终显示原始值而非人类可读的显示值,影响监控信息的可读性和调试。
📝 问题代码:
config = monitor_environment.monitor.config.copy()
💡 修复建议:
在返回字典中使用修改后的 config 副本,而不是原始配置。将 line 171 的 monitor_environment.monitor.config 替换为 config 变量。
✅ 修复示例:
return {
"id": str(monitor_environment.monitor.guid),
"slug": str(monitor_environment.monitor.slug),
"name": monitor_environment.monitor.name,
"config": config, # 使用修改后的 config 副本
"status": monitor_env.status,
"type": monitor_type,
}
🔗 参考链接
无
| ) | ||
|
|
||
| # reverse the list after slicing in order to start with oldest check-in | ||
| previous_checkins = list(reversed(previous_checkins[:failure_issue_threshold])) |
There was a problem hiding this comment.
🟡 AI 代码审查发现问题
📋 问题概述
阈值检查逻辑不完整
line 48 在截取 previous_checkins[:failure_issue_threshold] 后,没有验证返回的记录数量是否达到 failure_issue_threshold。如果数据库中符合条件的记录少于阈值,代码仍然会继续执行并可能错误地判定为 incident 状态。
例如:threshold=5 但数据库只有 3 条记录,这 3 条都是失败的,代码会错误地认为达到了 incident 阈值。
📍 问题详情
🟡 问题 1 | 严重程度: MEDIUM | 行号: 48
💬 详细说明:
- 在监控器历史数据不足的情况下,可能导致过早或错误地触发 incident,产生误报,影响运维人员对真实故障的判断。
📝 问题代码:
previous_checkins = list(reversed(previous_checkins[:failure_issue_threshold]))
💡 修复建议:
在截取列表后添加长度检查,确保记录数量达到阈值要求。修改建议:
previous_checkins = list(reversed(previous_checkins[:failure_issue_threshold]))
if len(previous_checkins) < failure_issue_threshold:
return False
✅ 修复示例:
# reverse the list after slicing in order to start with oldest check-in
previous_checkins = list(reversed(previous_checkins[:failure_issue_threshold]))
# Ensure we have enough check-ins to meet the threshold
if len(previous_checkins) < failure_issue_threshold:
return False
# If we have any successful check-ins within the threshold of
# commits we have NOT reached an incident state
if any([checkin["status"] == CheckInStatus.OK for checkin in previous_checkins]):
return False
🔗 参考链接
无
| monitor_env.status = MonitorStatus.ERROR | ||
| monitor_env.save(update_fields=("status",)) | ||
|
|
||
| starting_checkin = previous_checkins[0] |
There was a problem hiding this comment.
🟡 AI 代码审查发现问题
📋 问题概述
潜在 IndexError 风险
line 59 直接访问 previous_checkins[0],但没有检查列表是否为空。在 else 分支(line 36-53)中,当 failure_issue_threshold > 1 时,previous_checkins 来自数据库查询结果。如果查询返回空列表(例如新创建的 monitor 还没有 check-in 记录),previous_checkins[0] 将抛出 IndexError。
触发路径:line 27 条件为真 → line 36 else 分支 → line 48 查询返回空列表 → line 59 访问 [0] 崩溃。
📍 问题详情
🟡 问题 1 | 严重程度: MEDIUM | 行号: 59
💬 详细说明:
- 对于新创建的监控器或历史数据被清理的监控器,当发生失败时会导致事故处理流程崩溃,无法创建 incident 记录,影响监控系统的可靠性。
📝 问题代码:
starting_checkin = previous_checkins[0]
💡 修复建议:
在访问 previous_checkins[0] 前添加空列表检查。如果列表为空,应该返回 False 或采取其他适当的错误处理措施。修改建议:
if not previous_checkins:
return False
starting_checkin = previous_checkins[0]
✅ 修复示例:
if not previous_checkins:
logger.warning(
"No previous check-ins found for monitor environment %s",
monitor_env.id
)
return False
starting_checkin = previous_checkins[0]
🔗 参考链接
无
Since we'll be doing more with issue occurrences split out the concept
of incidents into it's own logic module, as well as incident_occurrence
into it's own module
Part of GH-80527