Skip to content

Commit 94b7028

Browse files
authored
chore(supergroups): Make backfill tuning params configurable via options (#113074)
Move batch_size, inter_batch_delay_s, and max_failures_per_batch from hardcoded constants to sentry-options so they can be tuned at runtime without a deploy. We also increased the pace of the backfill as it was conservative before, defaults reflect new faster backfill values (40, 5s, 20).
1 parent 7900d40 commit 94b7028

File tree

3 files changed

+62
-23
lines changed

3 files changed

+62
-23
lines changed

src/sentry/options/defaults.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,24 @@
13871387
default=False,
13881388
flags=FLAG_MODIFIABLE_BOOL | FLAG_AUTOMATOR_MODIFIABLE,
13891389
)
1390+
register(
1391+
"seer.supergroups_backfill_lightweight.batch_size",
1392+
type=Int,
1393+
default=40,
1394+
flags=FLAG_AUTOMATOR_MODIFIABLE,
1395+
)
1396+
register(
1397+
"seer.supergroups_backfill_lightweight.inter_batch_delay_s",
1398+
type=Int,
1399+
default=5,
1400+
flags=FLAG_AUTOMATOR_MODIFIABLE,
1401+
)
1402+
register(
1403+
"seer.supergroups_backfill_lightweight.max_failures_per_batch",
1404+
type=Int,
1405+
default=20,
1406+
flags=FLAG_AUTOMATOR_MODIFIABLE,
1407+
)
13901408

13911409
# ## sentry.killswitches
13921410
#

src/sentry/tasks/seer/backfill_supergroups_lightweight.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@
2525

2626
logger = logging.getLogger(__name__)
2727

28-
BATCH_SIZE = 25
29-
INTER_BATCH_DELAY_S = 10
30-
MAX_FAILURES_PER_BATCH = 10
31-
3228

3329
@instrumented_task(
3430
name="sentry.tasks.seer.backfill_supergroups_lightweight.backfill_supergroups_lightweight_for_org",
@@ -77,6 +73,21 @@ def _backfill_org(
7773
last_project_id: int,
7874
last_group_id: int,
7975
) -> None:
76+
batch_size: int = options.get("seer.supergroups_backfill_lightweight.batch_size")
77+
inter_batch_delay_s: int = options.get(
78+
"seer.supergroups_backfill_lightweight.inter_batch_delay_s"
79+
)
80+
max_failures_per_batch: int = options.get(
81+
"seer.supergroups_backfill_lightweight.max_failures_per_batch"
82+
)
83+
84+
if batch_size <= 0:
85+
logger.error(
86+
"supergroups_backfill_lightweight.invalid_batch_size",
87+
extra={"organization_id": organization_id, "batch_size": batch_size},
88+
)
89+
return
90+
8091
# Get the next project to process, starting from where we left off
8192
project = (
8293
Project.objects.filter(
@@ -107,7 +118,7 @@ def _backfill_org(
107118
substatus__in=UNRESOLVED_SUBSTATUS_CHOICES,
108119
)
109120
.select_related("project", "project__organization")
110-
.order_by("id")[:BATCH_SIZE]
121+
.order_by("id")[:batch_size]
111122
)
112123

113124
if not groups:
@@ -118,7 +129,7 @@ def _backfill_org(
118129
"last_project_id": project.id + 1,
119130
"last_group_id": 0,
120131
},
121-
countdown=INTER_BATCH_DELAY_S,
132+
countdown=inter_batch_delay_s,
122133
headers={"sentry-propagate-traces": False},
123134
)
124135
return
@@ -182,7 +193,7 @@ def _backfill_org(
182193

183194
last_processed_group_id = group.id
184195

185-
if failure_count >= MAX_FAILURES_PER_BATCH:
196+
if max_failures_per_batch > 0 and failure_count >= max_failures_per_batch:
186197
logger.error(
187198
"supergroups_backfill_lightweight.max_failures_reached",
188199
extra={
@@ -214,11 +225,11 @@ def _backfill_org(
214225
},
215226
)
216227

217-
if failure_count >= MAX_FAILURES_PER_BATCH:
228+
if max_failures_per_batch > 0 and failure_count >= max_failures_per_batch:
218229
return
219230

220231
# Self-chain: more groups in this project, or move to next project
221-
if len(groups) == BATCH_SIZE:
232+
if len(groups) == batch_size:
222233
next_project_id = project.id
223234
next_group_id = groups[-1].id
224235
else:
@@ -231,7 +242,7 @@ def _backfill_org(
231242
"last_project_id": next_project_id,
232243
"last_group_id": next_group_id,
233244
},
234-
countdown=INTER_BATCH_DELAY_S,
245+
countdown=inter_batch_delay_s,
235246
headers={"sentry-propagate-traces": False},
236247
)
237248

tests/sentry/tasks/seer/test_backfill_supergroups_lightweight.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33

44
from sentry.models.group import DEFAULT_TYPE_ID
55
from sentry.tasks.seer.backfill_supergroups_lightweight import (
6-
BATCH_SIZE,
76
backfill_supergroups_lightweight_for_org,
87
)
98
from sentry.testutils.cases import TestCase
9+
10+
TEST_BATCH_SIZE = 5
1011
from sentry.testutils.helpers.features import with_feature
1112
from sentry.types.group import GroupSubStatus
1213

@@ -81,7 +82,7 @@ def test_self_chains_when_more_groups_exist(self, mock_request):
8182
mock_request.return_value = MagicMock(status=200)
8283

8384
# Create enough groups to fill a batch
84-
for i in range(BATCH_SIZE):
85+
for i in range(TEST_BATCH_SIZE):
8586
evt = self.store_event(
8687
data={
8788
"message": f"error {i}",
@@ -94,9 +95,12 @@ def test_self_chains_when_more_groups_exist(self, mock_request):
9495
evt.group.substatus = GroupSubStatus.NEW
9596
evt.group.save(update_fields=["substatus"])
9697

97-
with patch(
98-
"sentry.tasks.seer.backfill_supergroups_lightweight.backfill_supergroups_lightweight_for_org.apply_async"
99-
) as mock_chain:
98+
with (
99+
self.options({"seer.supergroups_backfill_lightweight.batch_size": TEST_BATCH_SIZE}),
100+
patch(
101+
"sentry.tasks.seer.backfill_supergroups_lightweight.backfill_supergroups_lightweight_for_org.apply_async"
102+
) as mock_chain,
103+
):
100104
backfill_supergroups_lightweight_for_org(self.organization.id)
101105

102106
mock_chain.assert_called_once()
@@ -224,8 +228,8 @@ def test_resumes_from_cursor(self, mock_request):
224228
def test_chains_then_completes_on_exact_batch_boundary(self, mock_request):
225229
mock_request.return_value = MagicMock(status=200)
226230

227-
# Create exactly BATCH_SIZE groups total (setUp already created 1)
228-
for i in range(BATCH_SIZE - 1):
231+
# Create exactly TEST_BATCH_SIZE groups total (setUp already created 1)
232+
for i in range(TEST_BATCH_SIZE - 1):
229233
evt = self.store_event(
230234
data={
231235
"message": f"error {i}",
@@ -239,9 +243,12 @@ def test_chains_then_completes_on_exact_batch_boundary(self, mock_request):
239243
evt.group.save(update_fields=["substatus"])
240244

241245
# First call: full batch, chains with same project and group cursor
242-
with patch(
243-
"sentry.tasks.seer.backfill_supergroups_lightweight.backfill_supergroups_lightweight_for_org.apply_async"
244-
) as mock_chain:
246+
with (
247+
self.options({"seer.supergroups_backfill_lightweight.batch_size": TEST_BATCH_SIZE}),
248+
patch(
249+
"sentry.tasks.seer.backfill_supergroups_lightweight.backfill_supergroups_lightweight_for_org.apply_async"
250+
) as mock_chain,
251+
):
245252
backfill_supergroups_lightweight_for_org(self.organization.id)
246253
mock_chain.assert_called_once()
247254
next_kwargs = mock_chain.call_args.kwargs["kwargs"]
@@ -250,9 +257,12 @@ def test_chains_then_completes_on_exact_batch_boundary(self, mock_request):
250257

251258
# Second call: no groups left in project, chains to next project
252259
mock_request.reset_mock()
253-
with patch(
254-
"sentry.tasks.seer.backfill_supergroups_lightweight.backfill_supergroups_lightweight_for_org.apply_async"
255-
) as mock_chain:
260+
with (
261+
self.options({"seer.supergroups_backfill_lightweight.batch_size": TEST_BATCH_SIZE}),
262+
patch(
263+
"sentry.tasks.seer.backfill_supergroups_lightweight.backfill_supergroups_lightweight_for_org.apply_async"
264+
) as mock_chain,
265+
):
256266
backfill_supergroups_lightweight_for_org(self.organization.id, **next_kwargs)
257267
mock_request.assert_not_called()
258268
mock_chain.assert_called_once()

0 commit comments

Comments
 (0)