Sentry Workflow Engine 数据模型深度解析:检测、条件、工作流与运行时状态
【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry
导读
本文基于 Sentry 开源仓库中 workflow_engine 数据模型文档 展开,深入剖析新一代 Workflow Engine 的持久化图结构:从DataSource到Detector的检测链路、条件与条件组的三种角色、Workflow及其动作连接,再到DetectorState、WorkflowFireHistory等运行时状态表。读完本文,你将掌握各模型之间的关联方式、所有权约束与不变量、生命周期与缓存失效规则,以及项目迁移(relocation)与测试覆盖的实践要点,可直接对照仓库源码继续深入。
一、总体概念图:Workflow Engine 的持久化图
Workflow Engine 用一组相互关联的 Django 模型描述“检测 → 条件判断 → 工作流触发 → 动作执行”的完整链路。官方文档用一张 ER 图概括全貌(概念图,省略了兼容模型),其核心关系包括:
- 组织(Organization)拥有数据源与工作流;项目(Project)拥有检测器(Detector);
DataSourceDetector是数据源与检测器之间的多对多映射;- 检测器拥有触发器条件组(
Detector.workflow_condition_group),触发组内包含若干检测条件; DetectorWorkflow连接检测器与工作流,而工作流归组织所有;- 工作流拥有 WHEN 条件组与
WorkflowDataConditionGroup(IF 组),IF 组内包含 IF 条件并通过DataConditionGroupAction门控动作; - 检测器维护
DetectorState(运行状态)与DetectorGroup(关联 Issue Platform Group); WorkflowActionGroupStatus与WorkflowFireHistory分别负责重复频率抑制与触发历史记录。
缩略名与实际模型的对应关系
ER 图中的缩略名是条件角色(condition roles)而非模型名,这一点最容易造成混淆:
| ER 图名称 | 实际模型与挂载点 |
|---|---|
WHEN_GROUP/WHEN_CONDITION | Workflow.when_condition_group指向的DataConditionGroup/DataCondition |
IF_GROUP/IF_CONDITION | WorkflowDataConditionGroup.condition_group指向的条件组及其条件 |
DETECTOR_TRIGGER_GROUP | Detector.workflow_condition_group指向的条件组 |
关于运行时控制流,官方文档指引参见 Execution。
二、检测图(Detection Graph)
1.DataSource:通用数据源引用
DataSource归属于某个组织(organization外键),并以通用方式引用一个会产出检测器输入的产品模型。其关键设计:
(type, source_id)组合在数据库层面全局唯一(见UniqueConstraint(fields=["type", "source_id"], name="unique_type_source_id")),组织不参与该唯一约束;type在data_source_type_registry中注册,选中的DataSourceTypeHandler提供通用引用无法表达的额外行为;source_id使用文本类型,因为被引用的源模型主键类型不统一(如监控器、订阅、uptime 订阅),调用方必须通过注册的 handler 来解析,而不能自行假设它指向哪个模型。
从源码看,DataSource.__relocation_dependencies__显式声明了三种已知源类型依赖:monitors.monitor(cron 监控器)、sentry.querysubscription(Snuba 查询订阅)、uptime.uptimesubscription(uptime 订阅),与文档所述“Snuba 查询订阅、uptime 订阅、cron 监控器”一致。数据库层为(organization, type, source_id)建立了复合索引以支持按源快速反查。
2.DataSourceDetector:源与检测器的多对多映射
DataSourceDetector是数据源与检测器之间的查找表(through model),其约束(data_source, detector)唯一(workflow_engine_uniq_datasource_detector)。
- 一个数据源可以喂给多个检测器;
- 一个检测器也可以按所属产品模型的规则连接多条源记录;
- 检测器按数据源查找是有缓存的,因此任何变更都必须走 receiver 驱动的失效路径(详见下文“生命周期与失效”)。
3.Detector:检测的配置单元
Detector是配置好的检测单元,核心字段与关系如下:
| 字段 / 关系 | 含义 |
|---|---|
project | 普通检测器的所属项目;可空,用于组织级 issue-stream 检测器(project__isnull=True且type=IssueStreamGroupType.slug时通过config__organization_id定位组织,见DetectorQuerySet.by_organization) |
type | Issue PlatformGroupType的 slug |
workflow_condition_group | 触发器条件组,将输入映射到优先级等级;字段可空且唯一 |
config | 检测器类型专属设置,通过DetectorSettings.config_schema(JSON Schema)校验,由JSONConfigBase提供校验能力 |
enabled | 用户控制的启用/静默(snooze)状态,db_default=True |
status | 生命周期状态,包括待删除(ObjectStatus.PENDING_DELETION、DELETION_IN_PROGRESS)与套餐级禁用(ObjectStatus.DISABLED) |
检测器类型即GroupType注册表
检测器类型在 Django app 之外注册:具体的GroupType提供DetectorSettings(定义于 types.py),其中可定义:
- 运行时 handler(
handler); - API 校验器(
validator); - 配置 JSON schema(
config_schema); - 检测器可见性的查询过滤器(
detector_type_filters)。
文档明确警告:不要再建第二个检测器注册表,GroupType注册就是检测器类型的注册表。源码中Detector.detector_handler与Detector.settings属性正是通过grouptype.registry.get_by_slug(self.type)解析,找不到时抛出ValueError。
无唯一约束与并发创建
Detector不强制(project, type)的数据库唯一约束。默认检测器的创建依赖短分布式锁 + 幂等查找(见 defaults/detectors.py,使用sentry.locks),Detector.get_default_detector_for_project还借助缓存(CACHE_TTL = 600秒)减少查询。Manager 的get_queryset默认排除待删除/删除中的记录。
触发器条件组:历史命名的字段
检测器的workflow_condition_group指向一个DataConditionGroup,该关系可空且唯一(unique=True, on_delete=models.SET_NULL),独立于 Workflow 侧的条件关系。文档提醒:字段名是历史遗留——它虽然叫workflow_condition_group,实际属于检测器求值,与 Workflow 无关。
4. 检测链路中的缓存键与快照
- 缓存键:
get_detector_project_type_cache_key生成detector:by_proj_type:{project_id}:{detector_type}; - 快照:
Detector.get_snapshot()返回{id, type, enabled, status, trigger_condition},DetectorSnapshot中trigger_condition是DataConditionGroupSnapshot; - 条件读取:
Detector.get_conditions()优先复用已缓存的条件组,否则用单条查询DataCondition.objects.filter(condition_group__detector=self)避免 N+1。
三、条件体系:DataCondition与DataConditionGroup
1.DataCondition:单条逻辑条件
DataCondition的字段如下:
| 字段 | 含义 |
|---|---|
type | 存储的条件类型(Condition枚举,如eq、gte、lt、every_event、issue_priority_equals等) |
comparison | JSON 比较值/配置 |
condition_result | 命中时返回的 JSON 结果 |
condition_group | 必填的父条件组外键(on_delete=CASCADE) |
求值机制(evaluate_value):
- 基础比较运算(
eq/gt/gte/lt/lte/ne)走CONDITION_OPS映射到operator.eq/ge/gt/le/lt/ne直接求值; - 其余类型通过
condition_handler_registry找到DataConditionHandler执行handler.evaluate_value(value, comparison); - 布尔结果会转换为
condition_result(命中)或None(未命中);结果类型约束为DataConditionResult = DetectorPriorityLevel | int | float | bool | None,非法值返回ConditionError; - 求值被
scopedstats.timer()与metrics.timer("workflow_engine.data_condition.evaluation_duration")观测,慢条件(事件频率类)会被打上speed_category=slow标签。
Schema 校验:enforce_data_condition_json_schema对非基础运算类型使用 handler 的comparison_json_schema校验comparison字段。
2.DataConditionGroup:逻辑组合
DataConditionGroup拥有组织(organization外键,on_delete=CASCADE)与存储的逻辑类型logic_type:
logic_type | 语义 |
|---|---|
any | 求值所有条件,任一命中即为真 |
any-short | 一旦有条件命中即短路停止 |
all | 全部命中才为真 |
none | 全部未命中才为真,任一命中立即返回假 |
条件组存在三种预期角色:
- 检测器触发器组(
Detector.workflow_condition_group); - Workflow 的 WHEN 组(
Workflow.when_condition_group); - Workflow 的 IF/动作过滤组(
WorkflowDataConditionGroup.condition_group)。
文档强调:这些关系之间不强制角色互斥,通用校验器也不校验 handler 的放置位置,必须靠创建代码按约定保持角色分离。规范化的求值、校验与放置契约参见 Conditions。
四、工作流图(Workflow Graph)
1.DetectorWorkflow:检测器 ↔ 工作流
DetectorWorkflow是连接检测器与工作流的 join 模型,(detector, workflow)唯一(unique_together),两端on_delete=CASCADE。它决定:与某个检测器关联的 issue 事件,哪些工作流是候选。
一个 issue 事件可以按多个检测器维度查询候选工作流:
- 已知的具体检测器;
- 项目级的 issue-stream 检测器;
- 可选的、组织级 all-projects issue-stream 检测器。
这样工作流既可以精准命中产品专属检测器,也可以覆盖更广的 issue 流。
2.Workflow:组织作用域的工作流
Workflow是组织作用域(organization外键)模型,重要字段:
| 字段 / 关系 | 含义 |
|---|---|
when_condition_group | 可选的触发器组,在动作过滤之前求值;数据库层有唯一约束(workflow_engine_workflow_when_condition_group_id_..._uniq) |
environment | NULL表示所有环境,或指向某一个具体环境 |
config.frequency | 重复抑制间隔(分钟),JSON Schema 定义type: integer, minimum: 0;DEFAULT_FREQUENCY = 30 |
enabled | 是否可运行(db_default=True,即“snooze”工作流的手段) |
status | 删除状态追踪(Manager 排除待删除/删除中记录) |
evaluate_trigger_conditions的语义很关键:如果没有 WHEN 条件组,工作流视为默认触发(返回result=True, triggered=True);若条件组在删除/迁移中丢失,则记录异常并返回未触发 +ConditionError。调用方可以批量预取条件组传入以避免 N+1。get_slow_conditions用于收集 WHEN 组中的慢条件,便于在处理器中区分快慢路径。
3.WorkflowDataConditionGroup:工作流 → IF 组
WorkflowDataConditionGroup把一个工作流连接到一个 IF/动作过滤条件组;condition_group字段unique=True,因此一个条件组通过该关系只能属于一个工作流。
4.DataConditionGroupAction与Action:动作的连接与执行
DataConditionGroupAction把动作连接到 IF 组(带cond_group_action_idx索引);Action存储动作类型及 handler 专属配置。
- 动作类型(
Action.Type)包括slack、slack_staging、msteams、discord、pagerduty、opsgenie、github、github_enterprise、jira、jira_server、vsts、email、sentry_app、plugin、webhook,其中除 email/sentry_app/plugin/webhook 外均为集成动作(is_integration()返回 True,data中对应集成 key); - 动作类型通过
action_handler_registry选择 handler;handler 通常实现在所属的集成或通知包中,而非workflow_engine内部; - 一个动作可以被多个工作流可达;数据模型不定义动作执行顺序;
Action.trigger()组装ActionInvocation(event_data, action, detector, notification_uuid, workflow_id)并调用handler.execute(invocation),全程有workflow_engine.action.trigger.execution_time计时与计数指标;get_dedup_key()由type、integration_id、config(剔除target_display)与data组合出动作去重键。
五、运行时状态与历史(Runtime State and History)
1.DetectorState:持久化的检测器状态
DetectorState存储检测器与其可选DetectorGroupKey的持久化状态。detector_group_key让一个检测器可以为数据包中的多个值(例如不同端点)分别追踪独立状态。
关键约束:唯一约束detector_state_unique_group_key使用Coalesce("detector_group_key", Value("")),把 NULL 组键视为空值,从而保证未分组检测器只有一行状态。
状态跨存储拆分(运行时契约的一部分):
- PostgreSQL 持久化优先级(
state字段,默认DetectorPriorityLevel.OK,priority_level属性转换为枚举)与触发状态(is_triggered,数据库列名active,配detector_state_triggered_date部分索引); - Redis 存储去重水位线(dedupe watermarks)与优先级阈值计数器,带 TTL。
对于StatefulDetectorHandler,去重值必须是随源顺序递增的正整数;水位线缺省为 0,且 7 天后从 Redis 过期——意味着重复抑制不会超过该 TTL 持久化。
2.DetectorGroup:检测器 ↔ Issue Platform Group
DetectorGroup把 Issue Platform 的Group与产生/拥有它的检测器关联起来(unique_together = ("group",))。检测器外键可空(on_delete=SET_NULL),删除后既有 issue 历史依然有效。关联通常在 Issue Platform 摄取期间创建,兼容路径可回填缺失关联;从 issue 反查检测器的调用方必须处理检测器已删除或缺失的情况。
3.WorkflowActionGroupStatus:重复频率抑制
WorkflowActionGroupStatus记录(workflow, action, group)三元组上次通过重复频率过滤的时间,唯一约束workflow_engine_uniq_workflow_action_group。它是在事件级去重与任务执行之前更新的,因此不能证明外部动作确实完成。
4.WorkflowFireHistory:触发意图历史
WorkflowFireHistory存储 Workflow、issue group、event ID(CharField(max_length=32))、notification UUID(UUIDField(auto_add=True, unique=True))以及可选的 detector(on_delete=SET_NULL)。它没有动作或投递结果字段;行在非活跃动作过滤与事件级去重之前创建,所以记录的是派发前的意图,而非已调度或成功投递的动作。索引包括(workflow, date_added, group)与(date_added),便于按工作流/时间线回溯。
六、兼容模型(Compatibility Models)
兼容关联模型负责把遗留的 issue 告警与 metric 告警记录映射到 Detector、Workflow、条件、动作与 Issue Platform 记录上。它们不参与常规的 Detector/Workflow 求值。完整的模型清单与当前混合读写行为参见 Legacy alert API compatibility。
七、生命周期与失效(Lifecycle and Invalidation)
创建(Creation)
- Detector 模型 API 创建由
BaseDetectorTypeValidator协调:在一个事务内创建或连接检测器的条件图、产品专属源对象、通用数据源、源映射与工作流; - 默认检测器与工作流由 defaults/detectors.py 与 defaults/workflows.py 从项目/组织的 signal receiver 触发创建;这些函数必须保持幂等(配合分布式锁与缓存),实测覆盖见 test_detectors.py 与 test_workflows.py。
更新(Updates)
对检测器、工作流、条件、源映射或动作映射的更新会失效缓存的处理图。receivers/中的 receiver 通常用transaction.on_commit()调度失效,避免用未提交数据重建缓存。绕过校验器或关系模型的直写可能跳过 schema 校验、审计日志或缓存失效——官方建议优先走既有 validator 与服务路径。
删除(Deletion)
Detector API 删除会标记待删除(PENDING_DELETION)并调度 cell 删除任务;相关 Issue Platform groups 与历史 fire 记录可能比检测器活得更久,运行时查找必须容忍缺失记录。控制侧(control-silo)集成变更的动作清理走混合云ActionService。
八、作用域与迁移(Scope and Relocation)
Workflow Engine 模型位于cell silo(@cell_silo_model),并按声明的__relocation_scope__参与 Sentry 的 relocation 框架。特别地:
DataSource需要特殊归一化:其通用source_id必须用注册的 source handler 提供的模型名(handler.get_relocation_model_name())在normalize_before_relocation_import中重新映射主键(见 data_source.py);Detector、Workflow、DataCondition、DataConditionGroup、DataSourceDetector、DetectorWorkflow、WorkflowDataConditionGroup为RelocationScope.Organization;Action、DataConditionGroupAction、DetectorState、DetectorGroup、WorkflowActionGroupStatus、WorkflowFireHistory为RelocationScope.Excluded(不参与迁移)。
安全准则:不要仅凭任意关联 ID 推断组织所有权;API 查询必须显式限定到组织及其允许的项目(参考DetectorQuerySet.by_organization的实现模式)。
九、推荐测试与扩展阅读
官方文档推荐以下测试与工具作为深入验证的入口:
- test_organization_project_detector_index.py:演示事务化的检测器图创建;
- test_detectors.py:覆盖默认创建与锁;
- test_workflows.py:覆盖默认工作流图;
- receivers 测试目录:覆盖校验与缓存失效;
- project_transfer.py:项目迁移的克隆与重连辅助函数(目前没有专属处理器测试,是值得补足的空白点)。
配合阅读的关联文档还包括 Execution(运行时控制流)、Conditions(条件求值契约)与 Legacy alert API compatibility(兼容模型)。
十、核心要点速查
- 注册表即事实来源:
GroupType注册是检测器类型注册表,data_source_type_registry、condition_handler_registry、action_handler_registry(定义于 registry.py)分别解析数据源、条件与动作的行为; - 角色分离靠约定:
DataConditionGroup的三种角色(检测器触发组 / WHEN 组 / IF 组)不由数据库强制,创建代码必须自觉隔离; - 状态分存:检测器优先级与触发状态在 PostgreSQL,去重水位线与阈值计数器在 Redis(TTL 7 天),两者共同构成运行时契约;
- 历史表记录意图:
WorkflowFireHistory与WorkflowActionGroupStatus在派发/去重前写入,不代表动作成功执行; - 删除是软删除:检测器删除标记
PENDING_DELETION并异步清理,group 与历史记录可能长存,运行时必须容忍缺失; - 迁移需重映射:
DataSource.source_id的通用引用在 relocation 导入时按 handler 模型名重建主键映射。
【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考