Skip to content

Commit 22777b3

Browse files
authored
fix(aci milestone 3): anomaly detection processing fixes (#95261)
- Update issue title - Fix a bug with AlertContext that prevented actions from firing - Add logs to investigate why alerts aren't firing through the legacy system once they make it through workflow engine processing
1 parent bb2438d commit 22777b3

File tree

3 files changed

+38
-8
lines changed

3 files changed

+38
-8
lines changed

src/sentry/incidents/grouptype.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ def construct_title(
113113
priority: DetectorPriorityLevel,
114114
) -> str:
115115
comparison_delta = self.detector.config.get("comparison_delta")
116+
detection_type = self.detector.config.get("detection_type")
116117
agg_display_key = snuba_query.aggregate
117118

118119
if is_mri_field(agg_display_key):
@@ -125,6 +126,10 @@ def construct_title(
125126
else:
126127
aggregate = QUERY_AGGREGATION_DISPLAY.get(agg_display_key, agg_display_key)
127128

129+
if detection_type == "dynamic":
130+
alert_type = aggregate
131+
return f"Detected an anomaly in the query for {alert_type}"
132+
128133
# Determine the higher or lower comparison
129134
higher_or_lower = ""
130135
if detector_trigger.type == Condition.GREATER:

src/sentry/incidents/subscription_processor.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,14 @@ def process_update(self, subscription_update: QuerySubscriptionUpdate) -> None:
453453
raise ResourceDoesNotExist("Detector not found, cannot evaluate anomaly")
454454

455455
is_anomalous = get_anomaly_evaluation_from_workflow_engine(detector, results)
456+
logger.info(
457+
"dual processing anomaly detection alert",
458+
extra={
459+
"rule_id": self.alert_rule.id,
460+
"detector_id": detector.id,
461+
"anomaly_evaluation": is_anomalous,
462+
},
463+
)
456464
if is_anomalous is None:
457465
# we only care about True and False — None indicates no change
458466
continue

src/sentry/incidents/typings/metric_detector.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,33 @@
3636
}
3737

3838

39-
def fetch_threshold_type(type: Condition) -> AlertRuleThresholdType:
40-
return CONDITION_TO_ALERT_RULE_THRESHOLD_TYPE[type]
39+
def fetch_threshold_type(condition: dict[str, Any]) -> AlertRuleThresholdType:
40+
condition_type = condition["type"]
41+
if condition_type == Condition.ANOMALY_DETECTION:
42+
return condition["comparison"]["threshold_type"]
43+
return CONDITION_TO_ALERT_RULE_THRESHOLD_TYPE[condition_type]
4144

4245

43-
def fetch_alert_threshold(comparison_value: float, group_status: GroupStatus) -> float | None:
46+
def fetch_alert_threshold(condition: dict[str, Any], group_status: GroupStatus) -> float | None:
47+
condition_type = condition["type"]
48+
if condition_type == Condition.ANOMALY_DETECTION:
49+
return None
50+
comparison_value = condition["comparison"]
4451
if group_status == GroupStatus.RESOLVED or group_status == GroupStatus.IGNORED:
4552
return None
4653
else:
4754
return comparison_value
4855

4956

50-
def fetch_resolve_threshold(comparison_value: float, group_status: GroupStatus) -> float | None:
57+
def fetch_resolve_threshold(condition: dict[str, Any], group_status: GroupStatus) -> float | None:
5158
"""
5259
This is the opposite of `fetch_alert_threshold`.
5360
We keep it explicitly separate to make it clear that we are fetching the resolve threshold and to consolidate tech debt.
5461
"""
62+
condition_type = condition["type"]
63+
if condition_type == Condition.ANOMALY_DETECTION:
64+
return None
65+
comparison_value = condition["comparison"]
5566
if group_status == GroupStatus.RESOLVED or group_status == GroupStatus.IGNORED:
5667
return comparison_value
5768
else:
@@ -79,6 +90,12 @@ class AlertContext:
7990
def from_alert_rule_incident(
8091
cls, alert_rule: AlertRule, alert_rule_threshold: float | None = None
8192
) -> AlertContext:
93+
resolve_threshold = alert_rule.resolve_threshold
94+
95+
if alert_rule.detection_type == AlertRuleDetectionType.DYNAMIC:
96+
alert_rule_threshold = None
97+
resolve_threshold = None
98+
8299
return cls(
83100
name=alert_rule.name,
84101
action_identifier_id=alert_rule.id,
@@ -87,7 +104,7 @@ def from_alert_rule_incident(
87104
comparison_delta=alert_rule.comparison_delta,
88105
sensitivity=alert_rule.sensitivity,
89106
alert_threshold=alert_rule_threshold,
90-
resolve_threshold=alert_rule.resolve_threshold,
107+
resolve_threshold=resolve_threshold,
91108
)
92109

93110
@classmethod
@@ -109,9 +126,9 @@ def from_workflow_engine_models(
109126
for cond in evidence_data.conditions
110127
if cond["condition_result"] == target_priority
111128
)
112-
threshold_type = fetch_threshold_type(Condition(condition["type"]))
113-
resolve_threshold = fetch_resolve_threshold(condition["comparison"], group_status)
114-
alert_threshold = fetch_alert_threshold(condition["comparison"], group_status)
129+
threshold_type = fetch_threshold_type(condition)
130+
resolve_threshold = fetch_resolve_threshold(condition, group_status)
131+
alert_threshold = fetch_alert_threshold(condition, group_status)
115132
sensitivity = fetch_sensitivity(condition)
116133
except StopIteration:
117134
raise ValueError("No threshold type found for metric issues")

0 commit comments

Comments
 (0)