Skip to content

Commit cffc4ff

Browse files
authored
chore(seer grouping): Stop using similarity-embeddings-grouping feature flag (#77116)
This removes all uses of the `projects:similarity-embeddings-grouping` feature flag, which has been superseded by the `sentry:similarity_backfill_completed` project option. The flag itself will be removed in a follow-up PR.
1 parent 08f269a commit cffc4ff

File tree

9 files changed

+84
-88
lines changed

9 files changed

+84
-88
lines changed

src/sentry/api/endpoints/project_details.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -954,9 +954,7 @@ def delete(self, request: Request, project) -> Response:
954954
project.rename_on_pending_deletion()
955955

956956
# Tell seer to delete all the project's grouping records
957-
if features.has(
958-
"projects:similarity-embeddings-grouping", project
959-
) or project.get_option("sentry:similarity_backfill_completed"):
957+
if project.get_option("sentry:similarity_backfill_completed"):
960958
call_seer_delete_project_grouping_records.apply_async(args=[project.id])
961959

962960
return Response(status=204)

src/sentry/grouping/ingest/seer.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import sentry_sdk
66
from django.conf import settings
77

8-
from sentry import features, options
8+
from sentry import options
99
from sentry import ratelimits as ratelimiter
1010
from sentry.conf.server import SEER_SIMILARITY_MODEL_VERSION
1111
from sentry.eventstore.models import Event
@@ -61,21 +61,19 @@ def should_call_seer_for_grouping(event: Event, primary_hashes: CalculatedHashes
6161

6262

6363
def _project_has_similarity_grouping_enabled(project: Project) -> bool:
64-
has_seer_grouping_flag_on = features.has("projects:similarity-embeddings-grouping", project)
65-
6664
# TODO: This is a hack to get ingest to turn on for projects as soon as they're backfilled. When
6765
# the backfill script completes, we turn on this option, enabling ingest immediately rather than
6866
# forcing the project to wait until it's been manually added to a feature handler. Once all
6967
# projects have been backfilled, the option (and this check) can go away.
70-
has_been_backfilled = project.get_option("sentry:similarity_backfill_completed")
68+
has_been_backfilled = bool(project.get_option("sentry:similarity_backfill_completed"))
7169

7270
metrics.incr(
7371
"grouping.similarity.event_project_backfill_status",
7472
sample_rate=options.get("seer.similarity.metrics_sample_rate"),
75-
tags={"backfilled": bool(has_seer_grouping_flag_on or has_been_backfilled)},
73+
tags={"backfilled": has_been_backfilled},
7674
)
7775

78-
return has_seer_grouping_flag_on or has_been_backfilled
76+
return has_been_backfilled
7977

8078

8179
# TODO: Here we're including events with hybrid fingerprints (ones which are `{{ default }}`

src/sentry/tasks/delete_seer_grouping_records.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
from typing import Any
33

4-
from sentry import features, options
4+
from sentry import options
55
from sentry.models.group import Group
66
from sentry.models.grouphash import GroupHash
77
from sentry.seer.similarity.grouping_records import (
@@ -56,10 +56,7 @@ def call_delete_seer_grouping_records_by_hash(
5656
project = group.project if group else None
5757
if (
5858
project
59-
and (
60-
features.has("projects:similarity-embeddings-grouping", project)
61-
or project.get_option("sentry:similarity_backfill_completed")
62-
)
59+
and project.get_option("sentry:similarity_backfill_completed")
6360
and not killswitch_enabled(project.id)
6461
and not options.get("seer.similarity-embeddings-delete-by-hash-killswitch.enabled")
6562
):

tests/sentry/api/endpoints/test_project_details.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1591,13 +1591,13 @@ def test_internal_project(self):
15911591
model_name="Project", object_id=self.project.id
15921592
).exists()
15931593

1594-
@with_feature("projects:similarity-embeddings-grouping")
15951594
@mock.patch(
15961595
"sentry.tasks.delete_seer_grouping_records.call_seer_delete_project_grouping_records.apply_async"
15971596
)
15981597
def test_delete_project_and_delete_grouping_records(
15991598
self, mock_call_seer_delete_project_grouping_records
16001599
):
1600+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
16011601
self._delete_project_and_assert_deleted()
16021602
mock_call_seer_delete_project_grouping_records.assert_called_with(args=[self.project.id])
16031603

tests/sentry/api/helpers/test_group_index.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import UTC, datetime, timedelta
2+
from time import time
23
from unittest.mock import MagicMock, Mock, patch
34

45
import pytest
@@ -1085,7 +1086,6 @@ def test_delete_groups_simple(self, send_robust: Mock):
10851086
)
10861087
assert send_robust.called
10871088

1088-
@with_feature("projects:similarity-embeddings-grouping")
10891089
@patch(
10901090
"sentry.tasks.delete_seer_grouping_records.delete_seer_grouping_records_by_hash.apply_async"
10911091
)
@@ -1094,6 +1094,8 @@ def test_delete_groups_simple(self, send_robust: Mock):
10941094
def test_delete_groups_deletes_seer_records_by_hash(
10951095
self, send_robust: Mock, mock_logger: Mock, mock_delete_seer_grouping_records_by_hash
10961096
):
1097+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
1098+
10971099
groups = [self.create_group(), self.create_group()]
10981100
group_ids = [group.id for group in groups]
10991101
request = self.make_request(user=self.user, method="GET")

tests/sentry/deletions/test_group.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from time import time
12
from unittest import mock
23
from uuid import uuid4
34

@@ -16,7 +17,6 @@
1617
from sentry.tasks.deletion.groups import delete_groups
1718
from sentry.testutils.cases import SnubaTestCase, TestCase
1819
from sentry.testutils.helpers.datetime import before_now, iso_format
19-
from sentry.testutils.helpers.features import with_feature
2020

2121

2222
class DeleteGroupTest(TestCase, SnubaTestCase):
@@ -170,13 +170,13 @@ def test_cleanup(self, nodestore_delete_multi, os_environ):
170170

171171
assert nodestore_delete_multi.call_count == 0
172172

173-
@with_feature("projects:similarity-embeddings-grouping")
174173
@mock.patch(
175174
"sentry.tasks.delete_seer_grouping_records.delete_seer_grouping_records_by_hash.apply_async"
176175
)
177176
def test_delete_groups_delete_grouping_records_by_hash(
178177
self, mock_delete_seer_grouping_records_by_hash_apply_async
179178
):
179+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
180180
other_event = self.store_event(
181181
data={
182182
"event_id": "d" * 32,

tests/sentry/event_manager/grouping/test_seer_grouping.py

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from dataclasses import asdict
2+
from time import time
23
from typing import Any
34
from unittest.mock import MagicMock, patch
45

@@ -7,9 +8,7 @@
78
from sentry.models.grouphash import GroupHash
89
from sentry.seer.similarity.types import SeerSimilarIssueData
910
from sentry.testutils.cases import TestCase
10-
from sentry.testutils.helpers import Feature
1111
from sentry.testutils.helpers.eventprocessing import save_new_event
12-
from sentry.testutils.helpers.features import with_feature
1312
from sentry.testutils.pytest.mocking import capture_results
1413
from sentry.utils.types import NonNone
1514

@@ -50,45 +49,47 @@ def test_obeys_seer_similarity_flags(self):
5049
),
5150
):
5251

53-
with Feature({"projects:similarity-embeddings-grouping": False}):
54-
new_event = save_new_event({"message": "Adopt don't shop"}, self.project)
55-
56-
# We checked whether to make the call, but didn't go through with it
57-
assert should_call_seer_spy.call_count == 1
58-
assert get_seer_similar_issues_spy.call_count == 0
59-
60-
# No metadata stored, parent group not used (even though `should_group` is True)
61-
assert "seer_similarity" not in NonNone(new_event.group).data["metadata"]
62-
assert "seer_similarity" not in new_event.data
63-
assert new_event.group_id != existing_event.group_id
64-
65-
should_call_seer_spy.reset_mock()
66-
get_seer_similar_issues_spy.reset_mock()
67-
68-
with Feature({"projects:similarity-embeddings-grouping": True}):
69-
new_event = save_new_event({"message": "Maisey is silly"}, self.project)
70-
expected_metadata = {
71-
"similarity_model_version": SEER_SIMILARITY_MODEL_VERSION,
72-
"results": [asdict(seer_result_data)],
73-
}
74-
# In real life just filtering on group id wouldn't be enough to guarantee us a
75-
# single, specific GroupHash record, but since the database resets before each test,
76-
# here it's okay
77-
expected_grouphash = GroupHash.objects.filter(
78-
group_id=NonNone(existing_event.group_id)
79-
).first()
80-
81-
# We checked whether to make the call, and then made it
82-
assert should_call_seer_spy.call_count == 1
83-
assert get_seer_similar_issues_spy.call_count == 1
84-
85-
# Metadata returned and stored
86-
assert get_seer_similar_issues_return_values[0][0] == expected_metadata
87-
assert new_event.data["seer_similarity"] == expected_metadata
88-
89-
# Parent grouphash returned and parent group used
90-
assert get_seer_similar_issues_return_values[0][1] == expected_grouphash
91-
assert new_event.group_id == existing_event.group_id
52+
# Project option not set
53+
self.project.update_option("sentry:similarity_backfill_completed", None)
54+
new_event = save_new_event({"message": "Adopt don't shop"}, self.project)
55+
56+
# We checked whether to make the call, but didn't go through with it
57+
assert should_call_seer_spy.call_count == 1
58+
assert get_seer_similar_issues_spy.call_count == 0
59+
60+
# No metadata stored, parent group not used (even though `should_group` is True)
61+
assert "seer_similarity" not in NonNone(new_event.group).data["metadata"]
62+
assert "seer_similarity" not in new_event.data
63+
assert new_event.group_id != existing_event.group_id
64+
65+
should_call_seer_spy.reset_mock()
66+
get_seer_similar_issues_spy.reset_mock()
67+
68+
# Project option set
69+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
70+
new_event = save_new_event({"message": "Maisey is silly"}, self.project)
71+
expected_metadata = {
72+
"similarity_model_version": SEER_SIMILARITY_MODEL_VERSION,
73+
"results": [asdict(seer_result_data)],
74+
}
75+
# In real life just filtering on group id wouldn't be enough to guarantee us a
76+
# single, specific GroupHash record, but since the database resets before each test,
77+
# here it's okay
78+
expected_grouphash = GroupHash.objects.filter(
79+
group_id=NonNone(existing_event.group_id)
80+
).first()
81+
82+
# We checked whether to make the call, and then made it
83+
assert should_call_seer_spy.call_count == 1
84+
assert get_seer_similar_issues_spy.call_count == 1
85+
86+
# Metadata returned and stored
87+
assert get_seer_similar_issues_return_values[0][0] == expected_metadata
88+
assert new_event.data["seer_similarity"] == expected_metadata
89+
90+
# Parent grouphash returned and parent group used
91+
assert get_seer_similar_issues_return_values[0][1] == expected_grouphash
92+
assert new_event.group_id == existing_event.group_id
9293

9394
@patch("sentry.grouping.ingest.seer.should_call_seer_for_grouping", return_value=True)
9495
@patch("sentry.grouping.ingest.seer.get_seer_similar_issues", return_value=({}, None))
@@ -159,7 +160,6 @@ def test_stores_seer_results_in_metadata(self, _):
159160

160161
assert new_event.data["seer_similarity"] == expected_metadata
161162

162-
@with_feature("projects:similarity-embeddings-grouping")
163163
@patch("sentry.grouping.ingest.seer.should_call_seer_for_grouping", return_value=True)
164164
def test_assigns_event_to_neighbor_group_if_found(self, _):
165165
for use_optimized_grouping, existing_event_message, new_event_message in [
@@ -191,7 +191,6 @@ def test_assigns_event_to_neighbor_group_if_found(self, _):
191191

192192
mock_get_similarity_data.reset_mock()
193193

194-
@with_feature("projects:similarity-embeddings-grouping")
195194
@patch("sentry.grouping.ingest.seer.should_call_seer_for_grouping", return_value=True)
196195
def test_creates_new_group_if_no_neighbor_found(self, _):
197196
for use_optimized_grouping, existing_event_message, new_event_message in [

tests/sentry/grouping/ingest/test_seer.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from dataclasses import asdict
2+
from time import time
23
from unittest.mock import MagicMock, patch
34

45
from sentry.conf.server import SEER_SIMILARITY_MODEL_VERSION
@@ -8,9 +9,7 @@
89
from sentry.models.grouphash import GroupHash
910
from sentry.seer.similarity.types import SeerSimilarIssueData
1011
from sentry.testutils.cases import TestCase
11-
from sentry.testutils.helpers import Feature
1212
from sentry.testutils.helpers.eventprocessing import save_new_event
13-
from sentry.testutils.helpers.features import with_feature
1413
from sentry.testutils.helpers.options import override_options
1514
from sentry.utils.types import NonNone
1615

@@ -47,23 +46,17 @@ def setUp(self):
4746
self.primary_hashes = self.event.get_hashes()
4847

4948
def test_obeys_feature_enablement_check(self):
50-
for grouping_flag, backfill_completed_option, expected_result in [
51-
(False, None, False),
52-
(True, None, True),
53-
(False, 11211231, True),
54-
(True, 11211231, True),
55-
]:
56-
with Feature({"projects:similarity-embeddings-grouping": grouping_flag}):
57-
self.project.update_option(
58-
"sentry:similarity_backfill_completed", backfill_completed_option
59-
)
60-
assert (
61-
should_call_seer_for_grouping(self.event, self.primary_hashes)
62-
is expected_result
63-
), f"Case (grouping {grouping_flag}, backfill completed {backfill_completed_option}) failed."
49+
for backfill_completed_option, expected_result in [(None, False), (11211231, True)]:
50+
self.project.update_option(
51+
"sentry:similarity_backfill_completed", backfill_completed_option
52+
)
53+
assert (
54+
should_call_seer_for_grouping(self.event, self.primary_hashes) is expected_result
55+
), f"Case {backfill_completed_option} failed."
6456

65-
@with_feature("projects:similarity-embeddings-grouping")
6657
def test_obeys_content_filter(self):
58+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
59+
6760
for content_eligibility, expected_result in [(True, True), (False, False)]:
6861
with patch(
6962
"sentry.grouping.ingest.seer.event_content_is_seer_eligible",
@@ -74,26 +67,29 @@ def test_obeys_content_filter(self):
7467
is expected_result
7568
)
7669

77-
@with_feature("projects:similarity-embeddings-grouping")
7870
def test_obeys_global_seer_killswitch(self):
71+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
72+
7973
for killswitch_enabled, expected_result in [(True, False), (False, True)]:
8074
with override_options({"seer.global-killswitch.enabled": killswitch_enabled}):
8175
assert (
8276
should_call_seer_for_grouping(self.event, self.primary_hashes)
8377
is expected_result
8478
)
8579

86-
@with_feature("projects:similarity-embeddings-grouping")
8780
def test_obeys_similarity_service_killswitch(self):
81+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
82+
8883
for killswitch_enabled, expected_result in [(True, False), (False, True)]:
8984
with override_options({"seer.similarity-killswitch.enabled": killswitch_enabled}):
9085
assert (
9186
should_call_seer_for_grouping(self.event, self.primary_hashes)
9287
is expected_result
9388
)
9489

95-
@with_feature("projects:similarity-embeddings-grouping")
9690
def test_obeys_project_specific_killswitch(self):
91+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
92+
9793
for blocked_projects, expected_result in [([self.project.id], False), ([], True)]:
9894
with override_options(
9995
{"seer.similarity.grouping_killswitch_projects": blocked_projects}
@@ -103,8 +99,9 @@ def test_obeys_project_specific_killswitch(self):
10399
is expected_result
104100
)
105101

106-
@with_feature("projects:similarity-embeddings-grouping")
107102
def test_obeys_global_ratelimit(self):
103+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
104+
108105
for ratelimit_enabled, expected_result in [(True, False), (False, True)]:
109106
with patch(
110107
"sentry.grouping.ingest.seer.ratelimiter.backend.is_limited",
@@ -117,8 +114,9 @@ def test_obeys_global_ratelimit(self):
117114
is expected_result
118115
)
119116

120-
@with_feature("projects:similarity-embeddings-grouping")
121117
def test_obeys_project_ratelimit(self):
118+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
119+
122120
for ratelimit_enabled, expected_result in [(True, False), (False, True)]:
123121
with patch(
124122
"sentry.grouping.ingest.seer.ratelimiter.backend.is_limited",
@@ -133,8 +131,9 @@ def test_obeys_project_ratelimit(self):
133131
is expected_result
134132
)
135133

136-
@with_feature("projects:similarity-embeddings-grouping")
137134
def test_obeys_circuit_breaker(self):
135+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
136+
138137
for request_allowed, expected_result in [(True, True), (False, False)]:
139138
with patch(
140139
"sentry.grouping.ingest.seer.CircuitBreaker.should_allow_request",
@@ -145,8 +144,9 @@ def test_obeys_circuit_breaker(self):
145144
is expected_result
146145
)
147146

148-
@with_feature("projects:similarity-embeddings-grouping")
149147
def test_obeys_customized_fingerprint_check(self):
148+
self.project.update_option("sentry:similarity_backfill_completed", int(time()))
149+
150150
default_fingerprint_event = Event(
151151
project_id=self.project.id,
152152
event_id="11212012123120120415201309082013",

0 commit comments

Comments
 (0)