-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
chore(deletions): Remove old group deletion task #96457
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
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 |
---|---|---|
|
@@ -4311,78 +4311,55 @@ def assert_deleted_groups(self, groups: Sequence[Group]) -> None: | |
assert not Group.objects.filter(id=group.id).exists() | ||
assert not GroupHash.objects.filter(group_id=group.id).exists() | ||
|
||
@patch("sentry.eventstream.backend") | ||
def test_delete_by_id(self, mock_eventstream: MagicMock) -> None: | ||
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. This test does a few more things than testing that using |
||
eventstream_state = {"event_stream_state": str(uuid4())} | ||
mock_eventstream.start_delete_groups = Mock(return_value=eventstream_state) | ||
@patch("sentry.eventstream.snuba.SnubaEventStream._send") | ||
@patch("sentry.eventstream.snuba.datetime") | ||
def test_delete_by_id(self, mock_datetime: MagicMock, mock_send: MagicMock) -> None: | ||
fixed_datetime = datetime.now() | ||
mock_datetime.now.return_value = fixed_datetime | ||
|
||
group1 = self.create_group(status=GroupStatus.RESOLVED) | ||
group2 = self.create_group(status=GroupStatus.UNRESOLVED) | ||
group3 = self.create_group(status=GroupStatus.IGNORED) | ||
group4 = self.create_group( | ||
project=self.create_project(slug="foo"), | ||
status=GroupStatus.UNRESOLVED, | ||
) | ||
|
||
hashes = [] | ||
for g in group1, group2, group3, group4: | ||
hash = uuid4().hex | ||
hashes.append(hash) | ||
GroupHash.objects.create(project=g.project, hash=hash, group=g) | ||
groups = self.create_n_groups_with_hashes(2, project=self.project) | ||
group_ids = [group.id for group in groups] | ||
|
||
self.login_as(user=self.user) | ||
with self.feature("organizations:global-views"): | ||
response = self.get_response( | ||
qs_params={"id": [group1.id, group2.id], "group4": group4.id} | ||
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 think the original test introduced this non-useful parameter by mistake: |
||
) | ||
|
||
mock_eventstream.start_delete_groups.assert_called_once_with( | ||
group1.project_id, [group1.id, group2.id] | ||
) | ||
|
||
assert response.status_code == 204 | ||
|
||
assert Group.objects.get(id=group1.id).status == GroupStatus.PENDING_DELETION | ||
assert not GroupHash.objects.filter(group_id=group1.id).exists() | ||
|
||
assert Group.objects.get(id=group2.id).status == GroupStatus.PENDING_DELETION | ||
assert not GroupHash.objects.filter(group_id=group2.id).exists() | ||
|
||
assert Group.objects.get(id=group3.id).status != GroupStatus.PENDING_DELETION | ||
assert GroupHash.objects.filter(group_id=group3.id).exists() | ||
|
||
assert Group.objects.get(id=group4.id).status != GroupStatus.PENDING_DELETION | ||
assert GroupHash.objects.filter(group_id=group4.id).exists() | ||
|
||
Group.objects.filter(id__in=(group1.id, group2.id)).update(status=GroupStatus.UNRESOLVED) | ||
|
||
with self.tasks(): | ||
with self.feature("organizations:global-views"): | ||
response = self.get_response( | ||
qs_params={"id": [group1.id, group2.id], "group4": group4.id} | ||
) | ||
with self.tasks(), self.feature("organizations:global-views"): | ||
response = self.get_response(qs_params={"id": group_ids}) | ||
assert response.status_code == 204 | ||
|
||
# XXX(markus): Something is sending duplicated replacements to snuba -- | ||
# once from within tasks.deletions.groups and another time from | ||
# sentry.deletions.defaults.groups | ||
assert mock_eventstream.end_delete_groups.call_args_list == [ | ||
call(eventstream_state), | ||
call(eventstream_state), | ||
# Extract transaction_id from the first call | ||
transaction_id = mock_send.call_args_list[0][1]["extra_data"][0]["transaction_id"] | ||
|
||
assert mock_send.call_args_list == [ | ||
call( | ||
self.project.id, | ||
"start_delete_groups", | ||
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 start call. |
||
extra_data=( | ||
{ | ||
"transaction_id": transaction_id, | ||
"project_id": self.project.id, | ||
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. As I mentioned in one of my other PRs, |
||
"group_ids": group_ids, | ||
"datetime": json.datetime_to_str(fixed_datetime), | ||
}, | ||
), | ||
asynchronous=False, | ||
), | ||
call( | ||
self.project.id, | ||
"end_delete_groups", | ||
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 end call. |
||
extra_data=( | ||
{ | ||
"transaction_id": transaction_id, | ||
"project_id": self.project.id, | ||
"group_ids": group_ids, | ||
"datetime": json.datetime_to_str(fixed_datetime), | ||
}, | ||
), | ||
asynchronous=False, | ||
), | ||
] | ||
|
||
assert response.status_code == 204 | ||
|
||
assert not Group.objects.filter(id=group1.id).exists() | ||
assert not GroupHash.objects.filter(group_id=group1.id).exists() | ||
|
||
assert not Group.objects.filter(id=group2.id).exists() | ||
assert not GroupHash.objects.filter(group_id=group2.id).exists() | ||
|
||
assert Group.objects.filter(id=group3.id).exists() | ||
assert GroupHash.objects.filter(group_id=group3.id).exists() | ||
|
||
assert Group.objects.filter(id=group4.id).exists() | ||
assert GroupHash.objects.filter(group_id=group4.id).exists() | ||
for group in groups: | ||
assert not Group.objects.filter(id=group.id).exists() | ||
assert not GroupHash.objects.filter(group_id=group.id).exists() | ||
|
||
@patch("sentry.eventstream.backend") | ||
def test_delete_performance_issue_by_id(self, mock_eventstream: MagicMock) -> None: | ||
|
@@ -4417,85 +4394,6 @@ def test_bulk_delete_for_many_projects_without_option(self) -> None: | |
groups_1 = self.create_n_groups_with_hashes(2, project=self.project) | ||
groups_2 = self.create_n_groups_with_hashes(5, project=project_2) | ||
|
||
with ( | ||
self.tasks(), | ||
patch("sentry.deletions.tasks.groups.GROUP_CHUNK_SIZE", NEW_CHUNK_SIZE), | ||
patch("sentry.deletions.tasks.groups.logger") as mock_logger, | ||
patch( | ||
"sentry.api.helpers.group_index.delete.uuid4", | ||
side_effect=[self.get_mock_uuid("foo"), self.get_mock_uuid("bar")], | ||
), | ||
): | ||
self.login_as(user=self.user) | ||
response = self.get_success_response(qs_params={"query": ""}) | ||
assert response.status_code == 204 | ||
batch_1 = [g.id for g in groups_2[0:2]] | ||
batch_2 = [g.id for g in groups_2[2:4]] | ||
batch_3 = [g.id for g in groups_2[4:]] | ||
assert batch_1 + batch_2 + batch_3 == [g.id for g in groups_2] | ||
|
||
calls_by_project: dict[int, list[tuple[str, dict[str, Any]]]] = defaultdict(list) | ||
for log_call in mock_logger.info.call_args_list: | ||
calls_by_project[log_call[1]["extra"]["project_id"]].append(log_call) | ||
|
||
assert len(calls_by_project) == 2 | ||
assert calls_by_project[self.project.id] == [ | ||
call( | ||
"delete_groups.started", | ||
extra={ | ||
"object_ids_count": len(groups_1), | ||
"object_ids_current_batch": [g.id for g in groups_1], | ||
"first_id": groups_1[0].id, | ||
"project_id": self.project.id, | ||
"transaction_id": "bar", | ||
}, | ||
), | ||
] | ||
assert calls_by_project[project_2.id] == [ | ||
call( | ||
"delete_groups.started", | ||
extra={ | ||
"object_ids_count": 5, | ||
"object_ids_current_batch": batch_1, | ||
"first_id": batch_1[0], | ||
"project_id": project_2.id, | ||
"transaction_id": "foo", | ||
}, | ||
), | ||
call( | ||
"delete_groups.started", | ||
extra={ | ||
"object_ids_count": 3, | ||
"object_ids_current_batch": batch_2, | ||
"first_id": batch_2[0], | ||
"project_id": project_2.id, | ||
"transaction_id": "foo", | ||
}, | ||
), | ||
call( | ||
"delete_groups.started", | ||
extra={ | ||
"object_ids_count": 1, | ||
"object_ids_current_batch": batch_3, | ||
"first_id": batch_3[0], | ||
"project_id": project_2.id, | ||
"transaction_id": "foo", | ||
}, | ||
), | ||
] | ||
|
||
self.assert_deleted_groups(groups_1 + groups_2) | ||
|
||
def test_bulk_delete_for_many_projects_with_option(self) -> None: | ||
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. This test is now the default one. |
||
NEW_CHUNK_SIZE = 2 | ||
with ( | ||
self.options({"deletions.groups.use-new-task": True}), | ||
self.feature("organizations:global-views"), | ||
): | ||
project_2 = self.create_project(slug="baz", organization=self.organization) | ||
groups_1 = self.create_n_groups_with_hashes(2, project=self.project) | ||
groups_2 = self.create_n_groups_with_hashes(5, project=project_2) | ||
|
||
with ( | ||
self.tasks(), | ||
patch("sentry.api.helpers.group_index.delete.GROUP_CHUNK_SIZE", NEW_CHUNK_SIZE), | ||
|
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.
If we have Snuba events to delete we will call it here:
sentry/src/sentry/deletions/defaults/group.py
Lines 179 to 180 in f10b7e2
We have always been calling it more than once, this can be seen in the tests:
