Skip to content

Commit bff380d

Browse files
Revert "fix(deletions) Remove events when groups/projects are deleted (#41937)"
This reverts commit 9618872. Co-authored-by: asottile-sentry <[email protected]>
1 parent 3a8d41b commit bff380d

File tree

6 files changed

+37
-137
lines changed

6 files changed

+37
-137
lines changed

src/sentry/deletions/defaults/group.py

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import os
2-
from collections import defaultdict
32

4-
from more_itertools import flatten
5-
6-
from sentry import eventstore, eventstream, models, nodestore
3+
from sentry import eventstore, models, nodestore
74
from sentry.eventstore.models import Event
85

96
from ..base import BaseDeletionTask, BaseRelation, ModelDeletionTask, ModelRelation
@@ -47,11 +44,11 @@ class EventDataDeletionTask(BaseDeletionTask):
4744
Deletes nodestore data, EventAttachment and UserReports for group
4845
"""
4946

50-
# Number of events fetched from eventstore per chunk() call.
5147
DEFAULT_CHUNK_SIZE = 10000
5248

53-
def __init__(self, manager, groups, **kwargs):
54-
self.groups = groups
49+
def __init__(self, manager, group_id, project_id, **kwargs):
50+
self.group_id = group_id
51+
self.project_id = project_id
5552
self.last_event = None
5653
super().__init__(manager, **kwargs)
5754

@@ -68,77 +65,58 @@ def chunk(self):
6865
]
6966
)
7067

71-
project_groups = defaultdict(list)
72-
for group in self.groups:
73-
project_groups[group.project_id].append(group.id)
74-
75-
project_ids = list(project_groups.keys())
76-
group_ids = list(flatten(project_groups.values()))
77-
7868
events = eventstore.get_unfetched_events(
7969
filter=eventstore.Filter(
80-
conditions=conditions, project_ids=project_ids, group_ids=group_ids
70+
conditions=conditions, project_ids=[self.project_id], group_ids=[self.group_id]
8171
),
8272
limit=self.DEFAULT_CHUNK_SIZE,
8373
referrer="deletions.group",
8474
orderby=["-timestamp", "-event_id"],
8575
)
76+
8677
if not events:
87-
# Remove all group events now that their node data has been removed.
88-
for project_id, group_ids in project_groups.items():
89-
eventstream_state = eventstream.start_delete_groups(project_id, group_ids)
90-
eventstream.end_delete_groups(eventstream_state)
9178
return False
9279

9380
self.last_event = events[-1]
9481

9582
# Remove from nodestore
96-
node_ids = [Event.generate_node_id(event.project_id, event.event_id) for event in events]
83+
node_ids = [Event.generate_node_id(self.project_id, event.event_id) for event in events]
9784
nodestore.delete_multi(node_ids)
9885

9986
# Remove EventAttachment and UserReport *again* as those may not have a
10087
# group ID, therefore there may be dangling ones after "regular" model
10188
# deletion.
10289
event_ids = [event.event_id for event in events]
10390
models.EventAttachment.objects.filter(
104-
event_id__in=event_ids, project_id__in=project_ids
91+
event_id__in=event_ids, project_id=self.project_id
10592
).delete()
10693
models.UserReport.objects.filter(
107-
event_id__in=event_ids, project_id__in=project_ids
94+
event_id__in=event_ids, project_id=self.project_id
10895
).delete()
10996

11097
return True
11198

11299

113100
class GroupDeletionTask(ModelDeletionTask):
114-
# Delete groups in blocks of 1000. Using 1000 aims to
115-
# balance the number of snuba replacements with memory limits.
116-
DEFAULT_CHUNK_SIZE = 1000
117-
118-
def delete_bulk(self, instance_list):
119-
"""
120-
Group deletion operates as a quasi-bulk operation so that we don't flood
121-
snuba replacements with deletions per group.
122-
"""
123-
self.mark_deletion_in_progress(instance_list)
101+
def get_child_relations(self, instance):
102+
relations = []
124103

125-
group_ids = [group.id for group in instance_list]
126-
127-
# Remove child relations for all groups first.
128-
child_relations = []
129-
for model in _GROUP_RELATED_MODELS:
130-
child_relations.append(ModelRelation(model, {"group_id__in": group_ids}))
104+
relations.extend(
105+
[ModelRelation(m, {"group_id": instance.id}) for m in _GROUP_RELATED_MODELS]
106+
)
131107

132-
# If this isn't a retention cleanup also remove event data.
108+
# Skip EventDataDeletionTask if this is being called from cleanup.py
133109
if not os.environ.get("_SENTRY_CLEANUP"):
134-
child_relations.append(
135-
BaseRelation(params={"groups": instance_list}, task=EventDataDeletionTask)
110+
relations.extend(
111+
[
112+
BaseRelation(
113+
{"group_id": instance.id, "project_id": instance.project_id},
114+
EventDataDeletionTask,
115+
)
116+
]
136117
)
137118

138-
self.delete_children(child_relations)
139-
140-
# Remove group objects with children removed.
141-
return self.delete_instance_bulk(instance_list)
119+
return relations
142120

143121
def delete_instance(self, instance):
144122
from sentry import similarity

src/sentry/deletions/defaults/project.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,19 @@ def get_child_relations(self, instance):
5454
for m in model_list
5555
]
5656
)
57-
relations.append(ModelRelation(models.Group, {"project_id": instance.id}))
57+
58+
model_list = (models.GroupMeta, models.GroupResolution, models.GroupSnooze)
59+
relations.extend(
60+
[
61+
ModelRelation(m, {"group__project": instance.id}, ModelDeletionTask)
62+
for m in model_list
63+
]
64+
)
5865

5966
# Release needs to handle deletes after Group is cleaned up as the foreign
6067
# key is protected
6168
model_list = (
69+
models.Group,
6270
models.ReleaseProject,
6371
models.ReleaseProjectEnvironment,
6472
models.ProjectDebugFile,

src/sentry/utils/query.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,8 @@ def bulk_delete_objects(
222222
params.append(value)
223223

224224
for column, value in filters.items():
225-
if column.endswith("__in"):
226-
column, _ = column.split("__")
227-
query.append(f"{quote_name(column)} = ANY(%s)")
228-
params.append(value)
229-
else:
230-
query.append(f"{quote_name(column)} = %s")
231-
params.append(value)
225+
query.append(f"{quote_name(column)} = %s")
226+
params.append(value)
232227

233228
query = """
234229
delete from %(table)s

tests/sentry/deletions/test_group.py

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def setUp(self):
4949
project_id=self.project.id,
5050
)
5151

52-
self.keep_event = self.store_event(
52+
self.store_event(
5353
data={
5454
"event_id": self.event_id3,
5555
"timestamp": iso_format(before_now(minutes=1)),
@@ -102,31 +102,6 @@ def test_simple(self):
102102
assert not nodestore.get(self.node_id)
103103
assert not nodestore.get(self.node_id2)
104104
assert nodestore.get(self.node_id3), "Does not remove from second group"
105-
assert Group.objects.filter(id=self.keep_event.group_id).exists()
106-
107-
def test_simple_multiple_groups(self):
108-
other_event = self.store_event(
109-
data={
110-
"event_id": "d" * 32,
111-
"timestamp": iso_format(before_now(minutes=1)),
112-
"fingerprint": ["group3"],
113-
},
114-
project_id=self.project.id,
115-
)
116-
other_node_id = Event.generate_node_id(self.project.id, other_event.event_id)
117-
keep_node_id = Event.generate_node_id(self.project.id, self.keep_event.event_id)
118-
119-
group = self.event.group
120-
with self.tasks():
121-
delete_groups(object_ids=[group.id, other_event.group_id])
122-
123-
assert not Group.objects.filter(id=group.id).exists()
124-
assert not Group.objects.filter(id=other_event.group_id).exists()
125-
assert not nodestore.get(self.node_id)
126-
assert not nodestore.get(other_node_id)
127-
128-
assert Group.objects.filter(id=self.keep_event.group_id).exists()
129-
assert nodestore.get(keep_node_id)
130105

131106
@mock.patch("os.environ.get")
132107
@mock.patch("sentry.nodestore.delete_multi")

tests/sentry/deletions/test_project.py

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from sentry import eventstore
21
from sentry.models import (
32
Commit,
43
CommitAuthor,
@@ -10,7 +9,6 @@
109
GroupAssignee,
1110
GroupMeta,
1211
GroupResolution,
13-
GroupSeen,
1412
Project,
1513
ProjectDebugFile,
1614
Release,
@@ -21,7 +19,6 @@
2119
)
2220
from sentry.tasks.deletion import run_deletion
2321
from sentry.testutils import TransactionTestCase
24-
from sentry.testutils.helpers.datetime import before_now, iso_format
2522
from sentry.testutils.silo import region_silo_test
2623

2724

@@ -100,30 +97,3 @@ def test_simple(self):
10097
assert not ProjectDebugFile.objects.filter(id=dif.id).exists()
10198
assert not File.objects.filter(id=file.id).exists()
10299
assert not ServiceHook.objects.filter(id=hook.id).exists()
103-
104-
def test_delete_error_events(self):
105-
keeper = self.create_project(name="keeper")
106-
project = self.create_project(name="test")
107-
event = self.store_event(
108-
data={
109-
"timestamp": iso_format(before_now(minutes=1)),
110-
"message": "oh no",
111-
},
112-
project_id=project.id,
113-
)
114-
group = event.group
115-
group_seen = GroupSeen.objects.create(group=group, project=project, user=self.user)
116-
117-
deletion = ScheduledDeletion.schedule(project, days=0)
118-
deletion.update(in_progress=True)
119-
120-
with self.tasks():
121-
run_deletion(deletion.id)
122-
123-
assert not Project.objects.filter(id=project.id).exists()
124-
assert not GroupSeen.objects.filter(id=group_seen.id).exists()
125-
assert not Group.objects.filter(id=group.id).exists()
126-
127-
conditions = eventstore.Filter(project_ids=[project.id, keeper.id], group_ids=[group.id])
128-
events = eventstore.get_events(conditions)
129-
assert len(events) == 0

tests/sentry/utils/test_query.py

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from sentry.models import Team, User
1+
from sentry.models import User
22
from sentry.testutils import TestCase
3-
from sentry.utils.query import RangeQuerySetWrapper, bulk_delete_objects
3+
from sentry.utils.query import RangeQuerySetWrapper
44

55

66
class RangeQuerySetWrapperTest(TestCase):
@@ -26,29 +26,3 @@ def test_loop_and_delete(self):
2626
user.delete()
2727

2828
assert User.objects.all().count() == 0
29-
30-
31-
class BulkDeleteObjectsTest(TestCase):
32-
def setUp(self):
33-
super().setUp()
34-
Team.objects.all().delete()
35-
36-
def test_basic(self):
37-
total = 10
38-
records = []
39-
for _ in range(total):
40-
records.append(self.create_team())
41-
42-
result = bulk_delete_objects(Team, id__in=[r.id for r in records])
43-
assert result, "Could be more work to do"
44-
assert len(Team.objects.all()) == 0
45-
46-
def test_limiting(self):
47-
total = 10
48-
records = []
49-
for _ in range(total):
50-
records.append(self.create_team())
51-
52-
result = bulk_delete_objects(Team, id__in=[r.id for r in records], limit=5)
53-
assert result, "Still more work to do"
54-
assert len(Team.objects.all()) == 5

0 commit comments

Comments
 (0)