Skip to content

Commit adc9450

Browse files
authored
ref(workflow_engine): Refactor StatefulDetectorHandler + StatefulGroupingDetectorHandler (#92457)
# Description - Merge the Grouping handler back into the stateful handler, as I was progressing in refactoring it away, i found myself hardcoding `none` a lot for the shared helpers. So refactored this to merge them a bit more together, this will take away the difficulty for implementing a stateful detector handler, but not reduce all the complexity in the class (seems like a reasonable tradeoff for now) - Update the Metric Alert detector to use the new handler.
1 parent e89dcb0 commit adc9450

File tree

9 files changed

+258
-326
lines changed

9 files changed

+258
-326
lines changed

src/sentry/incidents/grouptype.py

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,16 @@
77
from sentry import features
88
from sentry.incidents.metric_alert_detector import MetricAlertsDetectorValidator
99
from sentry.incidents.models.alert_rule import AlertRuleDetectionType, ComparisonDeltaChoices
10-
from sentry.incidents.utils.types import MetricDetectorUpdate, QuerySubscriptionUpdate
10+
from sentry.incidents.utils.types import QuerySubscriptionUpdate
1111
from sentry.issues.grouptype import GroupCategory, GroupType
1212
from sentry.models.organization import Organization
1313
from sentry.ratelimits.sliding_windows import Quota
1414
from sentry.types.group import PriorityLevel
15-
from sentry.workflow_engine.handlers.detector import (
16-
DetectorOccurrence,
17-
StatefulGroupingDetectorHandler,
18-
)
15+
from sentry.workflow_engine.handlers.detector import DetectorOccurrence, StatefulDetectorHandler
1916
from sentry.workflow_engine.handlers.detector.base import EvidenceData
2017
from sentry.workflow_engine.models.data_source import DataPacket
2118
from sentry.workflow_engine.processors.data_condition_group import ProcessedDataConditionGroup
22-
from sentry.workflow_engine.types import DetectorGroupKey, DetectorPriorityLevel, DetectorSettings
19+
from sentry.workflow_engine.types import DetectorPriorityLevel, DetectorSettings
2320

2421
COMPARISON_DELTA_CHOICES: list[None | int] = [choice.value for choice in ComparisonDeltaChoices]
2522
COMPARISON_DELTA_CHOICES.append(None)
@@ -30,7 +27,7 @@ class MetricIssueEvidenceData(EvidenceData):
3027
alert_id: int
3128

3229

33-
class MetricAlertDetectorHandler(StatefulGroupingDetectorHandler[QuerySubscriptionUpdate, int]):
30+
class MetricAlertDetectorHandler(StatefulDetectorHandler[QuerySubscriptionUpdate, int]):
3431
def create_occurrence(
3532
self,
3633
evaluation_result: ProcessedDataConditionGroup,
@@ -47,22 +44,12 @@ def create_occurrence(
4744
)
4845
return occurrence, {}
4946

50-
@property
51-
def counter_names(self) -> list[str]:
52-
# Placeholder for now, this should be a list of counters that we want to update as we go above warning / critical
53-
return []
54-
5547
def extract_dedupe_value(self, data_packet: DataPacket[QuerySubscriptionUpdate]) -> int:
5648
return int(data_packet.packet.get("timestamp", datetime.now(UTC)).timestamp())
5749

5850
def extract_value(self, data_packet: DataPacket[QuerySubscriptionUpdate]) -> int:
5951
return data_packet.packet["values"]["value"]
6052

61-
def extract_group_values(
62-
self, data_packet: DataPacket[MetricDetectorUpdate]
63-
) -> dict[DetectorGroupKey, int]:
64-
return {None: data_packet.packet["values"]["value"]}
65-
6653

6754
# Example GroupType and detector handler for metric alerts. We don't create these issues yet, but we'll use something
6855
# like these when we're sending issues as alerts

src/sentry/incidents/subscription_processor.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
)
4747
from sentry.incidents.utils.types import (
4848
DATA_SOURCE_SNUBA_QUERY_SUBSCRIPTION,
49-
MetricDetectorUpdate,
5049
QuerySubscriptionUpdate,
5150
)
5251
from sentry.models.project import Project
@@ -336,13 +335,13 @@ def process_update(self, subscription_update: QuerySubscriptionUpdate) -> None:
336335

337336
if aggregation_value is not None:
338337
if has_metric_alert_processing:
339-
packet = MetricDetectorUpdate(
338+
packet = QuerySubscriptionUpdate(
340339
entity=subscription_update.get("entity", ""),
341340
subscription_id=subscription_update["subscription_id"],
342341
values={"value": aggregation_value},
343342
timestamp=self.last_update,
344343
)
345-
data_packet = DataPacket[MetricDetectorUpdate](
344+
data_packet = DataPacket[QuerySubscriptionUpdate](
346345
source_id=str(self.subscription.id), packet=packet
347346
)
348347
# temporarily skip processing any anomaly detection alerts

src/sentry/incidents/utils/types.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@ class QuerySubscriptionUpdate(TypedDict):
1010
timestamp: datetime
1111

1212

13-
class MetricDetectorUpdate(TypedDict):
14-
entity: str
15-
subscription_id: str
16-
values: Any
17-
timestamp: datetime
18-
19-
2013
class AlertRuleActivationConditionType(Enum):
2114
RELEASE_CREATION = 0
2215
DEPLOY_CREATION = 1

src/sentry/workflow_engine/handlers/detector/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"DetectorHandler",
55
"DetectorOccurrence",
66
"DetectorStateData",
7-
"StatefulGroupingDetectorHandler",
7+
"StatefulDetectorHandler",
88
]
99

1010
from .base import DataPacketEvaluationType, DataPacketType, DetectorHandler, DetectorOccurrence
11-
from .stateful import DetectorStateData, StatefulGroupingDetectorHandler
11+
from .stateful import DetectorStateData, StatefulDetectorHandler

src/sentry/workflow_engine/handlers/detector/base.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
DataPacketType = TypeVar("DataPacketType")
2323
DataPacketEvaluationType = TypeVar("DataPacketEvaluationType")
2424

25-
# TODO - get more info about how this is used in issue platform
2625
EventData = dict[str, Any]
2726

2827

@@ -79,7 +78,6 @@ def to_issue_occurrence(
7978
)
8079

8180

82-
# TODO - DetectorHandler -> AbstractDetectorHandler? (then DetectorHandler is the base implementation)
8381
class DetectorHandler(abc.ABC, Generic[DataPacketType, DataPacketEvaluationType]):
8482
def __init__(self, detector: Detector):
8583
self.detector = detector
@@ -125,19 +123,20 @@ def create_occurrence(
125123
pass
126124

127125
@abc.abstractmethod
128-
def extract_value(self, data_packet: DataPacket[DataPacketType]) -> DataPacketEvaluationType:
126+
def extract_value(
127+
self, data_packet: DataPacket[DataPacketType]
128+
) -> DataPacketEvaluationType | dict[DetectorGroupKey, DataPacketEvaluationType]:
129129
"""
130130
Extracts the evaluation value from the data packet to be processed.
131131
132132
This value is used to determine if the data condition group is in a triggered state.
133133
"""
134134
pass
135135

136-
# TODO should this be a required method? :thinking:
137136
@abc.abstractmethod
138137
def extract_dedupe_value(self, data_packet: DataPacket[DataPacketType]) -> int:
139138
"""
140-
Extracts the deduplication value from a passed data packet. This duplication
139+
Extracts the de-duplication value from a passed data packet. This duplication
141140
value is used to determine if we've already processed data to this point or not.
142141
143142
This is normally a timestamp, but could be any sortable value; (e.g. a sequence number, timestamp, etc).

0 commit comments

Comments
 (0)