Skip to content

Commit e9e09e1

Browse files
authored
ref(deletions): Remove unnecessary Seer calls in endpoint (#93541)
During the deletion API call we were calling seer & a couple more changes which the Group deletion model already does, thus, we don't need to do it during the API call. Prior to this change, if the Seer call failed, the deletion task would not get scheduled, thus, many groups stay in a pending deletion state and never deleted. Fixes #93509 Fixes [ID-716](https://linear.app/getsentry/issue/ID-716/move-endpoint-code-to-the-deletion-task)
1 parent 03b7eb7 commit e9e09e1

File tree

6 files changed

+14
-66
lines changed

6 files changed

+14
-66
lines changed

src/sentry/api/helpers/group_index/delete.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,10 @@
1313
from sentry import audit_log, eventstream
1414
from sentry.api.base import audit_logger
1515
from sentry.deletions.tasks.groups import delete_groups as delete_groups_task
16-
from sentry.issues.grouptype import GroupCategory
1716
from sentry.models.group import Group, GroupStatus
18-
from sentry.models.grouphash import GroupHash
1917
from sentry.models.groupinbox import GroupInbox
2018
from sentry.models.project import Project
2119
from sentry.signals import issue_deleted
22-
from sentry.tasks.delete_seer_grouping_records import may_schedule_task_to_delete_hashes_from_seer
2320
from sentry.utils.audit import create_audit_entry
2421

2522
from . import BULK_MUTATION_LIMIT, SearchFunction
@@ -47,12 +44,7 @@ def delete_group_list(
4744
# deterministic sort for sanity, and for very large deletions we'll
4845
# delete the "smaller" groups first
4946
group_list.sort(key=lambda g: (g.times_seen, g.id))
50-
group_ids = []
51-
error_ids = []
52-
for g in group_list:
53-
group_ids.append(g.id)
54-
if g.issue_category == GroupCategory.ERROR:
55-
error_ids.append(g.id)
47+
group_ids = [g.id for g in group_list]
5648

5749
transaction_id = uuid4().hex
5850
delete_logger.info(
@@ -84,13 +76,6 @@ def delete_group_list(
8476
# fails, we will still have a record of who requested the deletion.
8577
create_audit_entries(request, project, group_list, delete_type, transaction_id)
8678

87-
# Tell seer to delete grouping records for these groups
88-
may_schedule_task_to_delete_hashes_from_seer(error_ids)
89-
90-
# Removing GroupHash rows prevents new events from associating to the groups
91-
# we just deleted.
92-
GroupHash.objects.filter(project_id=project.id, group__id__in=group_ids).delete()
93-
9479
# We remove `GroupInbox` rows here so that they don't end up influencing queries for
9580
# `Group` instances that are pending deletion
9681
GroupInbox.objects.filter(project_id=project.id, group__id__in=group_ids).delete()

src/sentry/deletions/defaults/group.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import logging
34
import os
45
from collections import defaultdict
56
from collections.abc import Mapping, Sequence
@@ -21,6 +22,8 @@
2122
from ..base import BaseDeletionTask, BaseRelation, ModelDeletionTask, ModelRelation
2223
from ..manager import DeletionTaskManager
2324

25+
logger = logging.getLogger(__name__)
26+
2427
# Group models that relate only to groups and not to events. We assume those to
2528
# be safe to delete/mutate within a single transaction for user-triggered
2629
# actions (delete/reprocess/merge/unmerge)
@@ -261,20 +264,6 @@ def delete_bulk(self, instance_list: Sequence[Group]) -> bool:
261264
return True
262265

263266
self.mark_deletion_in_progress(instance_list)
264-
265-
error_group_ids = []
266-
# XXX: If a group type has been removed, we shouldn't error here.
267-
# Ideally, we should refactor `issue_category` to return None if the type is
268-
# unregistered.
269-
for group in instance_list:
270-
try:
271-
if group.issue_category == GroupCategory.ERROR:
272-
error_group_ids.append(group.id)
273-
except InvalidGroupTypeError:
274-
pass
275-
# Tell seer to delete grouping records with these group hashes
276-
may_schedule_task_to_delete_hashes_from_seer(error_group_ids)
277-
278267
self._delete_children(instance_list)
279268

280269
# Remove group objects with children removed.
@@ -291,6 +280,9 @@ def _delete_children(self, instance_list: Sequence[Group]) -> None:
291280

292281
error_groups, issue_platform_groups = separate_by_group_category(instance_list)
293282

283+
# Tell seer to delete grouping records with these group hashes
284+
may_schedule_task_to_delete_hashes_from_seer([group.id for group in error_groups])
285+
294286
# If this isn't a retention cleanup also remove event data.
295287
if not os.environ.get("_SENTRY_CLEANUP"):
296288
if error_groups:

tests/sentry/api/helpers/test_group_index.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,7 +1174,8 @@ def test_delete_groups_simple(self, send_robust: Mock):
11741174
GroupHash.objects.create(project=self.project, group=group, hash=hashes[i])
11751175
add_group_to_inbox(group, GroupInboxReason.NEW)
11761176

1177-
delete_groups(request, [self.project], self.organization.id)
1177+
with self.tasks():
1178+
delete_groups(request, [self.project], self.organization.id)
11781179

11791180
assert (
11801181
len(GroupHash.objects.filter(project_id=self.project.id, group_id__in=group_ids).all())
@@ -1205,7 +1206,8 @@ def test_delete_groups_deletes_seer_records_by_hash(
12051206
GroupHash.objects.create(project=self.project, group=group, hash=hashes[i])
12061207
add_group_to_inbox(group, GroupInboxReason.NEW)
12071208

1208-
delete_groups(request, [self.project], self.organization.id)
1209+
with self.tasks():
1210+
delete_groups(request, [self.project], self.organization.id)
12091211

12101212
assert (
12111213
len(GroupHash.objects.filter(project_id=self.project.id, group_id__in=group_ids).all())

tests/sentry/issues/endpoints/test_group_details.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -878,10 +878,7 @@ def test_delete_deferred(self) -> None:
878878

879879
# Deletion was deferred, so it should still exist
880880
assert Group.objects.get(id=group.id).status == GroupStatus.PENDING_DELETION
881-
# BUT the hash should be gone
882-
assert not GroupHash.objects.filter(group_id=group.id).exists()
883-
884-
Group.objects.filter(id=group.id).update(status=GroupStatus.UNRESOLVED)
881+
assert GroupHash.objects.filter(group_id=group.id).exists()
885882

886883
def test_delete_and_tasks_run(self) -> None:
887884
self.login_as(user=self.user)

tests/sentry/issues/endpoints/test_organization_group_index.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5602,7 +5602,7 @@ def create_n_groups(self, n: int, type: int | None = None) -> list[Group]:
56025602
def assert_pending_deletion_groups(self, groups: Sequence[Group]) -> None:
56035603
for group in groups:
56045604
assert Group.objects.get(id=group.id).status == GroupStatus.PENDING_DELETION
5605-
assert not GroupHash.objects.filter(group_id=group.id).exists()
5605+
assert GroupHash.objects.filter(group_id=group.id).exists()
56065606

56075607
def assert_deleted_groups(self, groups: Sequence[Group]) -> None:
56085608
for group in groups:
@@ -5639,12 +5639,7 @@ def test_delete_by_id(self, mock_eventstream: MagicMock) -> None:
56395639
)
56405640

56415641
assert response.status_code == 204
5642-
5643-
assert Group.objects.get(id=group1.id).status == GroupStatus.PENDING_DELETION
5644-
assert not GroupHash.objects.filter(group_id=group1.id).exists()
5645-
5646-
assert Group.objects.get(id=group2.id).status == GroupStatus.PENDING_DELETION
5647-
assert not GroupHash.objects.filter(group_id=group2.id).exists()
5642+
self.assert_pending_deletion_groups([group1, group2])
56485643

56495644
assert Group.objects.get(id=group3.id).status != GroupStatus.PENDING_DELETION
56505645
assert GroupHash.objects.filter(group_id=group3.id).exists()

tests/snuba/api/endpoints/test_project_group_index.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,7 +1505,6 @@ def create_groups(
15051505
def assert_groups_being_deleted(self, groups: Sequence[Group]) -> None:
15061506
for g in groups:
15071507
assert Group.objects.get(id=g.id).status == GroupStatus.PENDING_DELETION
1508-
assert not GroupHash.objects.filter(group_id=g.id).exists()
15091508

15101509
# This is necessary before calling the delete task
15111510
Group.objects.filter(id__in=[g.id for g in groups]).update(status=GroupStatus.UNRESOLVED)
@@ -1635,25 +1634,3 @@ def test_bulk_delete_performance_issues(self):
16351634
response = self.client.delete(url, format="json")
16361635
assert response.status_code == 204
16371636
self.assert_groups_are_gone(groups)
1638-
1639-
@patch("sentry.api.helpers.group_index.delete.may_schedule_task_to_delete_hashes_from_seer")
1640-
@patch("sentry.utils.audit.log_service.record_audit_log")
1641-
def test_audit_log_even_if_exception_raised(
1642-
self, mock_record_audit_log: Mock, mock_seer_delete: Mock
1643-
):
1644-
"""
1645-
Test that audit log is created even if an exception is raised after the audit log is created.
1646-
"""
1647-
# Calling seer happens after creating the audit log entry
1648-
mock_seer_delete.side_effect = Exception("Seer error!")
1649-
group1 = self.create_group()
1650-
self.login_as(user=self.user)
1651-
url = f"{self.path}?id={group1.id}"
1652-
with self.tasks():
1653-
response = self.client.delete(url, format="json")
1654-
assert response.status_code == 500
1655-
1656-
self.assert_audit_log_entry([group1], mock_record_audit_log)
1657-
1658-
# They have been marked as pending deletion but the exception prevented their complete deletion
1659-
assert Group.objects.get(id=group1.id).status == GroupStatus.PENDING_DELETION

0 commit comments

Comments
 (0)