Skip to content

Commit 0ec3a68

Browse files
mifu67andrewshie-sentry
authored andcommitted
ref(aci milestone 3): rework incident/open period dual writes (#98396)
For the purposes of <new system, old models>, we only need to create an IncidentGroupOpenPeriod relationship if an org is single processing metric issues through the workflow engine. Otherwise, the incidents and open periods are created independently. If an org is single processing via the workflow engine, then we need to create and update incidents based on open period creation/group status updates, as we will no longer be creating incidents through the subscription processor. - Create: when saving a metric issue occurrence, create the incident/IGOP relationship if they don't already exist for the current open period - Update: update the incident when a metric issue group's priority changes - Close: close the incident when a metric issue group's status changes to RESOLVED
1 parent 8bd2e17 commit 0ec3a68

File tree

9 files changed

+311
-306
lines changed

9 files changed

+311
-306
lines changed

src/sentry/incidents/logic.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@
101101
from sentry.utils.not_set import NOT_SET, NotSet
102102
from sentry.utils.snuba import is_measurement
103103
from sentry.workflow_engine.endpoints.validators.utils import toggle_detector
104-
from sentry.workflow_engine.models import IncidentGroupOpenPeriod
105104
from sentry.workflow_engine.models.detector import Detector
106105

107106
# We can return an incident as "windowed" which returns a range of points around the start of the incident
@@ -182,14 +181,6 @@ def create_incident(
182181
incident_type=incident_type.value,
183182
)
184183

185-
# If this is a metric alert incident, check for pending group relationships
186-
if (
187-
alert_rule
188-
and incident_type == IncidentType.ALERT_TRIGGERED
189-
and features.has("organizations:incident-group-open-period-write", organization)
190-
):
191-
IncidentGroupOpenPeriod.create_pending_relationships_for_incident(incident, alert_rule)
192-
193184
return incident
194185

195186

src/sentry/incidents/subscription_processor.py

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
)
4242
from sentry.incidents.tasks import handle_trigger_action
4343
from sentry.incidents.utils.process_update_helpers import (
44+
calculate_event_date_from_update_date,
4445
get_comparison_aggregation_value,
4546
get_crash_rate_alert_metrics_aggregation_value_helper,
4647
)
@@ -631,25 +632,6 @@ def process_update(self, subscription_update: QuerySubscriptionUpdate) -> None:
631632
# before the next one then we might alert twice.
632633
self.update_alert_rule_stats()
633634

634-
def calculate_event_date_from_update_date(self, update_date: datetime) -> datetime:
635-
"""
636-
Calculates the date that an event actually happened based on the date that we
637-
received the update. This takes into account time window and threshold period.
638-
:return:
639-
"""
640-
# Subscriptions label buckets by the end of the bucket, whereas discover
641-
# labels them by the front. This causes us an off-by-one error with event dates,
642-
# so to prevent this we subtract a bucket off of the date.
643-
update_date -= timedelta(seconds=self.alert_rule.snuba_query.time_window)
644-
# We want to also subtract `frequency * (threshold_period - 1)` from the date.
645-
# This allows us to show the actual start of the event, rather than the date
646-
# of the last update that we received.
647-
return update_date - timedelta(
648-
seconds=(
649-
self.alert_rule.snuba_query.resolution * (self.alert_rule.threshold_period - 1)
650-
)
651-
)
652-
653635
def trigger_alert_threshold(
654636
self, trigger: AlertRuleTrigger, metric_value: float
655637
) -> IncidentTrigger | None:
@@ -695,7 +677,9 @@ def trigger_alert_threshold(
695677

696678
# Only create a new incident if we don't already have an active incident for the AlertRule
697679
if not self.active_incident:
698-
detected_at = self.calculate_event_date_from_update_date(self.last_update)
680+
detected_at = calculate_event_date_from_update_date(
681+
self.last_update, self.alert_rule.snuba_query, self.alert_rule.threshold_period
682+
)
699683
self.active_incident = create_incident(
700684
organization=self.alert_rule.organization,
701685
incident_type=IncidentType.ALERT_TRIGGERED,
@@ -767,7 +751,11 @@ def trigger_resolve_threshold(
767751
self.active_incident,
768752
IncidentStatus.CLOSED,
769753
status_method=IncidentStatusMethod.RULE_TRIGGERED,
770-
date_closed=self.calculate_event_date_from_update_date(self.last_update),
754+
date_closed=calculate_event_date_from_update_date(
755+
self.last_update,
756+
self.alert_rule.snuba_query,
757+
self.alert_rule.threshold_period,
758+
),
771759
)
772760
self.active_incident = None
773761
self.incident_trigger_map.clear()

src/sentry/incidents/utils/process_update_helpers.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,21 @@ def get_comparison_aggregation_value(
217217
return None
218218

219219
return (aggregation_value / comparison_aggregate) * 100
220+
221+
222+
def calculate_event_date_from_update_date(
223+
update_date: datetime, snuba_query: SnubaQuery, threshold_period: int = 1
224+
) -> datetime:
225+
"""
226+
Calculates the date that an event actually happened based on the date that we
227+
received the update. This takes into account time window and threshold period.
228+
:return:
229+
"""
230+
# Subscriptions label buckets by the end of the bucket, whereas discover
231+
# labels them by the front. This causes us an off-by-one error with event dates,
232+
# so to prevent this we subtract a bucket off of the date.
233+
update_date -= timedelta(seconds=snuba_query.time_window)
234+
# We want to also subtract `frequency * (threshold_period - 1)` from the date.
235+
# This allows us to show the actual start of the event, rather than the date
236+
# of the last update that we received.
237+
return update_date - timedelta(seconds=(snuba_query.resolution * (threshold_period - 1)))

src/sentry/issues/ingest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def save_issue_occurrence(
7474

7575
# Create IncidentGroupOpenPeriod relationship for metric issues
7676
if occurrence.type == MetricIssue and features.has(
77-
"organizations:incident-group-open-period-write", event.organization
77+
"organizations:workflow-engine-single-process-metric-issues", event.organization
7878
):
7979
open_period = get_latest_open_period(group_info.group)
8080
if open_period:

src/sentry/issues/priority.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ def update_priority(
4343
"""
4444
Update the priority of a group and record the change in the activity and group history.
4545
"""
46+
from sentry.incidents.grouptype import MetricIssue
47+
from sentry.workflow_engine.models.incident_groupopenperiod import (
48+
update_incident_activity_based_on_group_activity,
49+
)
4650

4751
if priority is None or group.priority == priority:
4852
return
@@ -58,6 +62,11 @@ def update_priority(
5862
"reason": reason,
5963
},
6064
)
65+
# TODO (aci cleanup): if the group corresponds to a metric issue, then update its incident activity
66+
# we will remove this once we've fully deprecated the Incident model
67+
if group.type == MetricIssue.type_id:
68+
update_incident_activity_based_on_group_activity(group, priority)
69+
6170
record_group_history(group, status=PRIORITY_TO_GROUP_HISTORY_STATUS[priority], actor=actor)
6271

6372
issue_update_priority.send_robust(

src/sentry/models/group.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,12 @@ def update_group_status(
446446
from_substatus: int | None = None,
447447
) -> None:
448448
"""For each groups, update status to `status` and create an Activity."""
449+
from sentry.incidents.grouptype import MetricIssue
449450
from sentry.models.activity import Activity
450451
from sentry.models.groupopenperiod import update_group_open_period
452+
from sentry.workflow_engine.models.incident_groupopenperiod import (
453+
update_incident_based_on_open_period_status_change,
454+
)
451455

452456
modified_groups_list = []
453457
selected_groups = Group.objects.filter(id__in=[g.id for g in groups]).exclude(
@@ -518,6 +522,10 @@ def update_group_status(
518522
new_status=GroupStatus.UNRESOLVED,
519523
)
520524

525+
# TODO (aci cleanup): remove this once we've deprecated the incident model
526+
if group.type == MetricIssue.type_id:
527+
update_incident_based_on_open_period_status_change(group, status)
528+
521529
def from_share_id(self, share_id: str) -> Group:
522530
if not share_id or len(share_id) != 32:
523531
raise Group.DoesNotExist

0 commit comments

Comments
 (0)