Skip to content

[perf][alerts] 操作日志权限过滤在分页前物化全部可见告警和事故 ID #4675

Description

@zhouzhuangjie

现象

用户只查看一页操作日志时,服务会先把其组织范围内全部告警 ID 和事故 ID 读取成两个 Python 列表,再把它们塞进日志查询。正常情况下分页查询应让数据库完成关联过滤;数据量大时,取一页日志也要搬运完整业务对象 ID 集合。

触发场景

operation_log-View 权限的用户调用操作日志列表或详情接口时触发。只要请求带有当前团队,权限函数就会在分页之前物化该团队可见的全部历史告警和事故 ID,与本次日志页大小无关。

业务影响

每次请求额外执行两次全量 ID 查询,Python 内存、数据传输和后续 IN 参数数量随可见告警 A 与事故 IO(A+I) 增长。大型组织可能遭遇超长 SQL、参数上限、计划退化或明显延迟,而最终只返回一个分页结果。

根因(设计层)

权限边界本可保持为数据库子查询,却在拼接日志过滤条件前被强制物化:

scoped_alert_ids = list(
    filter_alert_queryset_for_request(Alert.objects.all(), request)
    .values_list("alert_id", flat=True)
)
scoped_incident_ids = list(
    filter_incident_queryset_for_request(Incident.objects.all(), request)
    .values_list("incident_id", flat=True)
)

随后两个列表进入 target_id__in。同时 OperatorLog 只有 created_at 单列索引,没有匹配 target_typetarget_id 与分页排序的组合索引。

涉及文件

文件:行 说明
server/apps/alerts/utils/permission_scope.py:136 操作日志权限过滤入口
server/apps/alerts/utils/permission_scope.py:145 在分页前物化全部告警和事故 ID
server/apps/alerts/views/operator_log.py:23 列表与详情共用该权限 queryset
server/apps/alerts/models/operator_log.py:10 日志模型缺少匹配过滤路径的组合索引

调用链

flowchart TD
    A["GET 操作日志"] --> B["SystemLogModelViewSet.get_queryset<br/>operator_log.py"]
    B --> C["filter_operator_log_queryset_for_request<br/>permission_scope.py"]
    C --> D["物化全部可见 Alert ID"]
    C --> E["物化全部可见 Incident ID"]
    D --> F["OperatorLog target_id IN 过滤"]
    E --> F
    F -.-> G["最后执行分页"]
Loading

证据 / 复现

weops/master 第 145 至 146 行对两个 values_list 显式调用 list,第 150 至 155 行再生成两个 target_id__in 条件。视图第 23 至 31 行先取得该 queryset,分页由父类后续执行。模型第 30 行仅给 created_at 建索引。

建议分别构造 1k、10k、100k 个团队可见目标,统计查询数、生成 SQL 长度、数据库耗时和 Python 峰值内存。当前 ID 传输、内存及 IN 参数为 O(A+I);优化后不应出现 eager ID 查询,SQL 文本和 Python 内存应基本不随 A+I 增长。

建议修复方向

使用 SubqueryExists 保持两类权限过滤在数据库内完成,并根据实际查询计划增加 target_typetarget_idcreated_at 的组合索引。保持当前团队解析、组织 JSON 范围、Alert/Incident 分支、空范围返回 none、distinct 和默认倒序分页不变。

功能影响 / 回归风险

权限可见范围属于安全边界,虽然目标是等价的查询重写,仍应按迁移验证。用旧实现结果作为 oracle,覆盖超级管理员、无 current team、父子组织、数字/字符串 team ID、空范围、Alert/Incident 混合日志、详情访问和多页排序,确保既不扩大也不缩小既有合法可见集。先灰度比对新旧 ID 集合和查询计划;查询可用特性开关回退,索引可反向 migration 回滚。

严重性判断

这是操作日志每次访问都经过的权限过滤,成本在分页前按组织完整历史规模增长,且超长 IN 会同时影响应用和数据库;当前没有已测得的生产超时或权限错误,因此定为 Medium。

严重度:Medium 工作量:M

负责人:@zhaojinmeng

Metadata

Metadata

Assignees

Labels

tech-debt技术债巡检自动产出

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions