-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
feat(aci milestone 3): delete data in Seer when AD detector is deleted #101843
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9807ec3
4931e0b
2c8aecd
b19adc4
da76551
3e2db73
8dc00b0
c26a2c2
510a4aa
da0082b
8f7c691
f78d262
1b19f1d
0592a42
1859b62
df56f01
b52d7e0
360e1bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -755,6 +755,30 @@ def nullify_id(model: Model) -> None: | |
| ) | ||
|
|
||
|
|
||
| def delete_anomaly_detection_rule(snuba_query: SnubaQuery, alert_rule: AlertRule) -> None: | ||
| """ | ||
| Delete accompanying data in Seer for anomaly detection rules | ||
| """ | ||
| try: | ||
| source_id = QuerySubscription.objects.get(snuba_query_id=snuba_query.id).id | ||
| success = delete_rule_in_seer( | ||
| organization=alert_rule.organization, | ||
| source_id=source_id, | ||
| ) | ||
| if not success: | ||
| logger.error( | ||
| "Call to delete rule data in Seer failed", | ||
| extra={ | ||
| "source_id": source_id, | ||
| }, | ||
| ) | ||
| except QuerySubscription.DoesNotExist: | ||
| logger.exception( | ||
| "Snuba query missing query subscription", | ||
| extra={"snuba_query_id": snuba_query.id}, | ||
| ) | ||
|
|
||
|
|
||
| def update_alert_rule( | ||
| alert_rule: AlertRule, | ||
| query_type: SnubaQuery.Type | None = None, | ||
|
|
@@ -921,18 +945,8 @@ def update_alert_rule( | |
| alert_rule, project, snuba_query, updated_fields, updated_query_fields | ||
| ) | ||
| else: | ||
| # if this was a dynamic rule, delete the data in Seer | ||
| if alert_rule.detection_type == AlertRuleDetectionType.DYNAMIC: | ||
| success = delete_rule_in_seer( | ||
| alert_rule=alert_rule, | ||
| ) | ||
| if not success: | ||
| logger.error( | ||
| "Call to delete rule data in Seer failed", | ||
| extra={ | ||
| "rule_id": alert_rule.id, | ||
| }, | ||
| ) | ||
| delete_anomaly_detection_rule(snuba_query, alert_rule) | ||
| # if this alert was previously a dynamic alert, then we should update the rule to be ready | ||
| if alert_rule.status == AlertRuleStatus.NOT_ENOUGH_DATA.value: | ||
| alert_rule.update(status=AlertRuleStatus.PENDING.value) | ||
|
|
@@ -1088,20 +1102,11 @@ def delete_alert_rule( | |
| ) | ||
| subscriptions = _unpack_snuba_query(alert_rule).subscriptions.all() | ||
|
|
||
| if alert_rule.detection_type == AlertRuleDetectionType.DYNAMIC: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're now deleting the rule in Seer regardless of whether an incident exists or not - previously we had it behind that condition because we were deleting it in the post delete signal and due to the order of deletions there would still be incidents after the rule was deleted so this was the only way we could make sure a dynamic alert rule with incidents sent the data to Seer.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we're overriding the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have it here for the legacy API use case - if they hit this directly, it won't hit the detector validator code path right? Worst case Seer gets 2 requests to delete the same data, but the 2nd one would just fail because it's already gone.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, I see that we changed the validator and not the model itself, in which case this should stay. |
||
| delete_anomaly_detection_rule(alert_rule.snuba_query, alert_rule) | ||
|
|
||
| incidents = Incident.objects.filter(alert_rule=alert_rule) | ||
| if incidents.exists(): | ||
| # if this was a dynamic rule, delete the data in Seer | ||
| if alert_rule.detection_type == AlertRuleDetectionType.DYNAMIC: | ||
| success = delete_rule_in_seer( | ||
| alert_rule=alert_rule, | ||
| ) | ||
| if not success: | ||
| logger.error( | ||
| "Call to delete rule data in Seer failed", | ||
| extra={ | ||
| "rule_id": alert_rule.id, | ||
| }, | ||
| ) | ||
| AlertRuleActivity.objects.create( | ||
| alert_rule=alert_rule, | ||
| user_id=user.id if user else None, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,7 +36,6 @@ | |
| ActionService, | ||
| ActionTarget, | ||
| ) | ||
| from sentry.seer.anomaly_detection.delete_rule import delete_rule_in_seer | ||
| from sentry.snuba.models import QuerySubscription | ||
| from sentry.types.actor import Actor | ||
| from sentry.utils import metrics | ||
|
|
@@ -140,18 +139,6 @@ def clear_alert_rule_subscription_caches(cls, instance: AlertRule, **kwargs: Any | |
| for sub_id in subscription_ids | ||
| ) | ||
|
|
||
| @classmethod | ||
| def delete_data_in_seer(cls, instance: AlertRule, **kwargs: Any) -> None: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we now require the source id (the query subscription id) instead of the rule id we can't execute this in post delete because the query subscription is deleted by this point. Instead we call it in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The post_delete is used for cascade deletions (like when a project gets deleted), during which the code in logic.py will not be executed 😭 I don't think we can take this out of here
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if instance.detection_type == AlertRuleDetectionType.DYNAMIC: | ||
| success = delete_rule_in_seer(alert_rule=instance) | ||
| if not success: | ||
| logger.error( | ||
| "Call to delete rule data in Seer failed", | ||
| extra={ | ||
| "rule_id": instance.id, | ||
| }, | ||
| ) | ||
|
|
||
|
|
||
| @region_silo_model | ||
| class AlertRuleProjects(Model): | ||
|
|
@@ -624,7 +611,6 @@ class Meta: | |
|
|
||
|
|
||
| post_delete.connect(AlertRuleManager.clear_subscription_cache, sender=QuerySubscription) | ||
| post_delete.connect(AlertRuleManager.delete_data_in_seer, sender=AlertRule) | ||
| post_save.connect(AlertRuleManager.clear_subscription_cache, sender=QuerySubscription) | ||
| post_save.connect(AlertRuleManager.clear_alert_rule_subscription_caches, sender=AlertRule) | ||
| post_delete.connect(AlertRuleManager.clear_alert_rule_subscription_caches, sender=AlertRule) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,15 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import logging | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| from django.conf import settings | ||
| from urllib3.exceptions import MaxRetryError, TimeoutError | ||
|
|
||
| from sentry.conf.server import SEER_ALERT_DELETION_URL | ||
| from sentry.models.organization import Organization | ||
| from sentry.net.http import connection_from_url | ||
| from sentry.seer.anomaly_detection.types import DeleteAlertDataRequest | ||
| from sentry.seer.anomaly_detection.types import AlertInSeer, DataSourceType, DeleteAlertDataRequest | ||
| from sentry.seer.signed_seer_api import make_signed_seer_api_request | ||
| from sentry.utils import json | ||
| from sentry.utils.json import JSONDecodeError | ||
|
|
@@ -18,21 +21,51 @@ | |
| ) | ||
|
|
||
| if TYPE_CHECKING: | ||
| from sentry.incidents.models.alert_rule import AlertRule | ||
| from sentry.workflow_engine.models.detector import Detector | ||
|
|
||
|
|
||
| def delete_data_in_seer_for_detector(detector: Detector): | ||
| from sentry.incidents.models.alert_rule import AlertRuleDetectionType | ||
| from sentry.workflow_engine.models import DataSourceDetector | ||
|
|
||
| def delete_rule_in_seer(alert_rule: "AlertRule") -> bool: | ||
| data_source_detector = DataSourceDetector.objects.filter(detector_id=detector.id).first() | ||
| if not data_source_detector: | ||
| logger.error( | ||
| "No data source found for detector", | ||
| extra={ | ||
| "detector_id": detector.id, | ||
| }, | ||
| ) | ||
| return | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: The 🔍 Detailed AnalysisIf a detector has multiple associated data sources, the 💡 Suggested FixModify 🤖 Prompt for AI AgentDid we get this right? 👍 / 👎 to inform future reviews.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are no detectors w/ multiple data sources |
||
| organization = detector.project.organization | ||
|
|
||
| if detector.config.get("detection_type") == AlertRuleDetectionType.DYNAMIC: | ||
| success = delete_rule_in_seer( | ||
| source_id=int(data_source_detector.data_source.source_id), organization=organization | ||
| ) | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if not success: | ||
| logger.error( | ||
| "Call to delete rule data in Seer failed", | ||
| extra={ | ||
| "detector_id": detector.id, | ||
| }, | ||
| ) | ||
|
|
||
|
|
||
| def delete_rule_in_seer(source_id: int, organization: Organization) -> bool: | ||
| """ | ||
| Send a request to delete an alert rule from Seer. Returns True if the request was successful. | ||
| """ | ||
| body = DeleteAlertDataRequest( | ||
| organization_id=alert_rule.organization.id, | ||
| alert={"id": alert_rule.id}, | ||
| organization_id=organization.id, | ||
| alert=AlertInSeer( | ||
| id=None, source_id=source_id, source_type=DataSourceType.SNUBA_QUERY_SUBSCRIPTION | ||
| ), | ||
| ) | ||
| extra_data = { | ||
| "rule_id": alert_rule.id, | ||
| "source_id": source_id, | ||
| } | ||
|
|
||
| try: | ||
| response = make_signed_seer_api_request( | ||
| connection_pool=seer_anomaly_detection_connection_pool, | ||
|
|
@@ -46,7 +79,7 @@ def delete_rule_in_seer(alert_rule: "AlertRule") -> bool: | |
| ) | ||
| return False | ||
|
|
||
| if response.status > 400: | ||
| if response.status >= 400: | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| logger.error( | ||
| "Error when hitting Seer delete rule data endpoint", | ||
| extra={ | ||
|
|
@@ -75,7 +108,14 @@ def delete_rule_in_seer(alert_rule: "AlertRule") -> bool: | |
| return False | ||
|
|
||
| status = results.get("success") | ||
| if status is None or status is not True: | ||
| if status is None: | ||
| logger.error( | ||
| "Request to delete alert rule from Seer was unsuccessful", | ||
| extra=extra_data, | ||
| ) | ||
| return False | ||
| elif status is not True: | ||
| extra_data["message"] = results.get("message") | ||
| logger.error( | ||
| "Request to delete alert rule from Seer was unsuccessful", | ||
| extra=extra_data, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Handle Multiple Subscriptions Gracefully
QuerySubscription.objects.get(snuba_query_id=snuba_query.id)will raiseMultipleObjectsReturnedwhen an alert rule has multiple projects, since each project has its own subscription pointing to the sameSnubaQuery. The function should either use.first()instead of.get(), or iterate through all subscriptions if multiple Seer deletions are needed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no multi-project alert rules/detectors exist