1
1
import logging
2
- from collections import defaultdict
2
+ from enum import StrEnum
3
3
4
4
import sentry_sdk
5
5
6
6
from sentry import buffer
7
7
from sentry .db .models .manager .base_query_set import BaseQuerySet
8
+ from sentry .eventstore .models import GroupEvent
8
9
from sentry .utils import json , metrics
9
10
from sentry .workflow_engine .models import (
10
11
Action ,
11
12
DataCondition ,
12
13
DataConditionGroup ,
13
14
Detector ,
14
15
Workflow ,
15
- WorkflowDataConditionGroup ,
16
16
)
17
17
from sentry .workflow_engine .processors .action import filter_recently_fired_workflow_actions
18
18
from sentry .workflow_engine .processors .data_condition_group import evaluate_condition_group
24
24
WORKFLOW_ENGINE_BUFFER_LIST_KEY = "workflow_engine_delayed_processing_buffer"
25
25
26
26
27
- # TODO remove this method
28
- def get_data_condition_groups_to_fire (
29
- workflows : set [Workflow ], job : WorkflowJob
30
- ) -> dict [int , list [int ]]:
31
- workflow_action_groups : dict [int , list [int ]] = defaultdict (list )
27
+ class WorkflowDataConditionGroupType (StrEnum ):
28
+ ACTION_FILTER = "action_filter"
29
+ WORKFLOW_TRIGGER = "workflow_trigger"
32
30
33
- workflow_ids = {workflow .id for workflow in workflows }
34
-
35
- workflow_dcgs = WorkflowDataConditionGroup .objects .filter (
36
- workflow_id__in = workflow_ids
37
- ).select_related ("condition_group" , "workflow" )
38
-
39
- for workflow_dcg in workflow_dcgs :
40
- action_condition = workflow_dcg .condition_group
41
- evaluation , result , _ = evaluate_condition_group (action_condition , job )
42
-
43
- if evaluation :
44
- workflow_action_groups [workflow_dcg .workflow_id ].append (action_condition .id )
45
31
46
- return workflow_action_groups
47
-
48
-
49
- def enqueue_workflows (
50
- workflows : set [Workflow ],
51
- job : WorkflowJob ,
32
+ def enqueue_workflow (
33
+ workflow : Workflow ,
34
+ delayed_conditions : list [DataCondition ],
35
+ event : GroupEvent ,
36
+ source : WorkflowDataConditionGroupType ,
52
37
) -> None :
53
- event = job ["event" ]
54
38
project_id = event .group .project .id
55
- workflow_action_groups = get_data_condition_groups_to_fire (workflows , job )
56
39
57
- for workflow in workflows :
58
- buffer .backend .push_to_sorted_set (key = WORKFLOW_ENGINE_BUFFER_LIST_KEY , value = project_id )
59
-
60
- action_filters = workflow_action_groups .get (workflow .id , [])
61
- if not action_filters :
62
- continue
40
+ buffer .backend .push_to_sorted_set (key = WORKFLOW_ENGINE_BUFFER_LIST_KEY , value = project_id )
63
41
64
- action_filter_fields = ":" .join (map (str , action_filters ))
42
+ condition_groups = "," .join (
43
+ str (condition .condition_group_id ) for condition in delayed_conditions
44
+ )
65
45
66
- value = json .dumps ({"event_id" : event .event_id , "occurrence_id" : event .occurrence_id })
67
- buffer .backend .push_to_hash (
68
- model = Workflow ,
69
- filters = {"project" : project_id },
70
- field = f"{ workflow .id } :{ event .group .id } :{ action_filter_fields } " ,
71
- value = value ,
72
- )
46
+ value = json .dumps ({"event_id" : event .event_id , "occurrence_id" : event .occurrence_id })
47
+ buffer .backend .push_to_hash (
48
+ model = Workflow ,
49
+ filters = {"project" : project_id },
50
+ field = f"{ workflow .id } :{ event .group .id } :{ condition_groups } : { source } " ,
51
+ value = value ,
52
+ )
73
53
74
54
75
55
def evaluate_workflow_triggers (workflows : set [Workflow ], job : WorkflowJob ) -> set [Workflow ]:
76
56
triggered_workflows : set [Workflow ] = set ()
77
- workflows_to_enqueue : set [Workflow ] = set ()
78
57
79
58
for workflow in workflows :
80
59
evaluation , remaining_conditions = workflow .evaluate_trigger_conditions (job )
60
+
81
61
if remaining_conditions :
82
- workflows_to_enqueue .add (workflow )
62
+ enqueue_workflow (
63
+ workflow ,
64
+ remaining_conditions ,
65
+ job ["event" ],
66
+ WorkflowDataConditionGroupType .WORKFLOW_TRIGGER ,
67
+ )
83
68
else :
84
69
if evaluation :
85
- # Only add workflows that have no remaining conditions to check
86
70
triggered_workflows .add (workflow )
87
71
88
- if workflows_to_enqueue :
89
- enqueue_workflows (workflows_to_enqueue , job )
90
-
91
72
return triggered_workflows
92
73
93
74
@@ -96,7 +77,6 @@ def evaluate_workflows_action_filters(
96
77
job : WorkflowJob ,
97
78
) -> BaseQuerySet [Action ]:
98
79
filtered_action_groups : set [DataConditionGroup ] = set ()
99
- enqueued_conditions : list [DataCondition ] = []
100
80
101
81
# gets the list of the workflow ids, and then get the workflow_data_condition_groups for those workflows
102
82
workflow_ids = {workflow .id for workflow in workflows }
@@ -111,9 +91,15 @@ def evaluate_workflows_action_filters(
111
91
if remaining_conditions :
112
92
# If there are remaining conditions for the action filter to evaluate,
113
93
# then return the list of conditions to enqueue
114
- enqueued_conditions .extend (remaining_conditions )
94
+ condition_group = action_condition .workflowdataconditiongroup_set .first ()
95
+ if condition_group :
96
+ enqueue_workflow (
97
+ condition_group .workflow ,
98
+ remaining_conditions ,
99
+ job ["event" ],
100
+ WorkflowDataConditionGroupType .ACTION_FILTER ,
101
+ )
115
102
else :
116
- # if we don't have any other conditions to evaluate, add the action to the list
117
103
if evaluation :
118
104
filtered_action_groups .add (action_condition )
119
105
@@ -131,6 +117,8 @@ def process_workflows(job: WorkflowJob) -> set[Workflow]:
131
117
Next, it will evaluate the "when" (or trigger) conditions for each workflow, if the conditions are met,
132
118
the workflow will be added to a unique list of triggered workflows.
133
119
120
+ TODO @saponifi3d add metrics for this method
121
+
134
122
Finally, each of the triggered workflows will have their actions evaluated and executed.
135
123
"""
136
124
# Check to see if the GroupEvent has an issue occurrence
@@ -143,8 +131,14 @@ def process_workflows(job: WorkflowJob) -> set[Workflow]:
143
131
144
132
# Get the workflows, evaluate the when_condition_group, finally evaluate the actions for workflows that are triggered
145
133
workflows = set (Workflow .objects .filter (detectorworkflow__detector_id = detector .id ).distinct ())
146
- triggered_workflows = evaluate_workflow_triggers (workflows , job )
147
- actions = evaluate_workflows_action_filters (triggered_workflows , job )
134
+
135
+ with sentry_sdk .start_span (op = "workflow_engine.process_workflows.evaluate_workflow_triggers" ):
136
+ triggered_workflows = evaluate_workflow_triggers (workflows , job )
137
+
138
+ with sentry_sdk .start_span (
139
+ op = "workflow_engine.process_workflows.evaluate_workflows_action_filters"
140
+ ):
141
+ actions = evaluate_workflows_action_filters (triggered_workflows , job )
148
142
149
143
with sentry_sdk .start_span (op = "workflow_engine.process_workflows.trigger_actions" ):
150
144
for action in actions :
0 commit comments