Skip to content

Commit ec6b653

Browse files
getsentry-botshellmayr
authored andcommitted
Revert "fix(dynamic-sampling): use options instead of feature flags for switching b/w span and transaction based rebalancing (#102576)"
This reverts commit 8ecceb3. Co-authored-by: shellmayr <[email protected]>
1 parent cbac601 commit ec6b653

File tree

4 files changed

+42
-86
lines changed

4 files changed

+42
-86
lines changed

src/sentry/core/endpoints/organization_details.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,10 +1152,10 @@ def _compute_project_target_sample_rates(self, request: Request, organization: O
11521152
# so we need to refactor this into an async task we can run and observe
11531153
org_id = organization.id
11541154
measure = SamplingMeasure.TRANSACTIONS
1155-
if options.get("dynamic-sampling.check_span_feature_flag"):
1156-
span_org_ids = options.get("dynamic-sampling.measure.spans") or []
1157-
if org_id in span_org_ids:
1158-
measure = SamplingMeasure.SPANS
1155+
if options.get("dynamic-sampling.check_span_feature_flag") and features.has(
1156+
"organizations:dynamic-sampling-spans", organization
1157+
):
1158+
measure = SamplingMeasure.SPANS
11591159

11601160
projects_with_tx_count_and_rates = []
11611161
for chunk in query_project_counts_by_org(

src/sentry/dynamic_sampling/tasks/boost_low_volume_projects.py

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -133,33 +133,38 @@ def partition_by_measure(
133133

134134
# Exclude orgs with project-mode sampling from the start. We know the
135135
# default is DynamicSamplingMode.ORGANIZATION.
136-
filtered_org_ids = {
137-
org.id for org, mode in modes.items() if mode != DynamicSamplingMode.PROJECT
138-
}
136+
orgs = [org for org, mode in modes.items() if mode != DynamicSamplingMode.PROJECT]
139137

140138
if not options.get("dynamic-sampling.check_span_feature_flag"):
141-
metrics.incr(
142-
"dynamic_sampling.partition_by_measure.transactions", amount=len(filtered_org_ids)
143-
)
144-
return {SamplingMeasure.TRANSACTIONS: sorted(filtered_org_ids)}
139+
metrics.incr("dynamic_sampling.partition_by_measure.transactions", amount=len(orgs))
140+
return {SamplingMeasure.TRANSACTIONS: [org.id for org in orgs]}
145141

146-
span_org_ids = set(options.get("dynamic-sampling.measure.spans") or [])
147-
span_org_ids = span_org_ids & filtered_org_ids
148-
transactions_org_ids = filtered_org_ids - span_org_ids
142+
spans = []
143+
transactions = []
149144

150-
logger.info(
151-
"dynamic_sampling.partition_by_measure.options_check",
152-
extra={"span_org_ids": span_org_ids},
145+
# Use batch feature flag check to avoid N+1 queries.
146+
feature_results = features.batch_has_for_organizations(
147+
"organizations:dynamic-sampling-spans", orgs
153148
)
149+
if feature_results is None:
150+
metrics.incr("dynamic_sampling.partition_by_measure.transactions", amount=len(orgs))
151+
logger.error("dynamic_sampling.partition_by_measure.features_none", extra={"orgs": orgs})
152+
return {SamplingMeasure.TRANSACTIONS: [org.id for org in orgs]}
154153

155-
metrics.incr("dynamic_sampling.partition_by_measure.spans", amount=len(span_org_ids))
156-
metrics.incr(
157-
"dynamic_sampling.partition_by_measure.transactions", amount=len(transactions_org_ids)
154+
logger.info(
155+
"dynamic_sampling.partition_by_measure.batched_feature_check",
156+
extra={"feature_results": feature_results},
158157
)
159-
return {
160-
SamplingMeasure.SPANS: sorted(span_org_ids),
161-
SamplingMeasure.TRANSACTIONS: sorted(transactions_org_ids),
162-
}
158+
159+
for org in orgs:
160+
if feature_results.get(f"organization:{org.id}"):
161+
spans.append(org.id)
162+
else:
163+
transactions.append(org.id)
164+
165+
metrics.incr("dynamic_sampling.partition_by_measure.spans", amount=len(spans))
166+
metrics.incr("dynamic_sampling.partition_by_measure.transactions", amount=len(transactions))
167+
return {SamplingMeasure.SPANS: spans, SamplingMeasure.TRANSACTIONS: transactions}
163168

164169

165170
@instrumented_task(
@@ -185,10 +190,10 @@ def boost_low_volume_projects_of_org_with_query(org_id: OrganizationId) -> None:
185190
return
186191

187192
measure = SamplingMeasure.TRANSACTIONS
188-
if options.get("dynamic-sampling.check_span_feature_flag"):
189-
span_org_ids = options.get("dynamic-sampling.measure.spans") or []
190-
if org_id in span_org_ids:
191-
measure = SamplingMeasure.SPANS
193+
if options.get("dynamic-sampling.check_span_feature_flag") and features.has(
194+
"organizations:dynamic-sampling-spans", org
195+
):
196+
measure = SamplingMeasure.SPANS
192197

193198
projects_with_tx_count_and_rates = fetch_projects_with_total_root_transaction_count_and_rates(
194199
org_ids=[org_id],

src/sentry/options/defaults.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,14 +2181,6 @@
21812181
flags=FLAG_AUTOMATOR_MODIFIABLE | FLAG_MODIFIABLE_RATE,
21822182
)
21832183

2184-
# List of organization IDs that should be using spans for rebalancing in dynamic sampling.
2185-
register(
2186-
"dynamic-sampling.measure.spans",
2187-
default=[],
2188-
type=Sequence,
2189-
flags=FLAG_AUTOMATOR_MODIFIABLE,
2190-
)
2191-
21922184
# === Hybrid cloud subsystem options ===
21932185
# UI rollout
21942186
register(

tests/sentry/dynamic_sampling/tasks/test_boost_low_volume_projects.py

Lines changed: 9 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,9 @@ def test_complex(self) -> None:
287287
class TestPartitionByMeasure(TestCase):
288288
def test_partition_by_measure_with_spans_feature(self) -> None:
289289
org = self.create_organization("test-org1")
290-
with self.options(
291-
{
292-
"dynamic-sampling.check_span_feature_flag": True,
293-
"dynamic-sampling.measure.spans": [org.id],
294-
}
290+
with (
291+
self.options({"dynamic-sampling.check_span_feature_flag": True}),
292+
self.feature({"organizations:dynamic-sampling-spans": True}),
295293
):
296294
result = partition_by_measure([org.id])
297295
assert SamplingMeasure.SPANS in result
@@ -301,11 +299,9 @@ def test_partition_by_measure_with_spans_feature(self) -> None:
301299

302300
def test_partition_by_measure_without_spans_feature(self) -> None:
303301
org = self.create_organization("test-org1")
304-
with self.options(
305-
{
306-
"dynamic-sampling.check_span_feature_flag": True,
307-
"dynamic-sampling.measure.spans": [],
308-
}
302+
with (
303+
self.options({"dynamic-sampling.check_span_feature_flag": True}),
304+
self.feature({"organizations:dynamic-sampling-spans": False}),
309305
):
310306
result = partition_by_measure([org.id])
311307
assert SamplingMeasure.SPANS in result
@@ -315,48 +311,11 @@ def test_partition_by_measure_without_spans_feature(self) -> None:
315311

316312
def test_partition_by_measure_with_span_feature_flag_disabled(self) -> None:
317313
org = self.create_organization("test-org1")
318-
with self.options(
319-
{
320-
"dynamic-sampling.check_span_feature_flag": False,
321-
"dynamic-sampling.measure.spans": [org.id],
322-
}
314+
with (
315+
self.options({"dynamic-sampling.check_span_feature_flag": False}),
316+
self.feature({"organizations:dynamic-sampling-spans": True}),
323317
):
324318
result = partition_by_measure([org.id])
325319
assert SamplingMeasure.TRANSACTIONS in result
326320
assert SamplingMeasure.SPANS not in result
327321
assert result[SamplingMeasure.TRANSACTIONS] == [org.id]
328-
329-
def test_partition_by_measure_returns_sorted_output_multiple_orgs(self) -> None:
330-
orgs = [self.create_organization(f"test-org{i}") for i in range(10)]
331-
org_ids = [org.id for org in reversed(orgs)]
332-
333-
with self.options(
334-
{
335-
"dynamic-sampling.check_span_feature_flag": True,
336-
"dynamic-sampling.measure.spans": [orgs[2].id, orgs[7].id, orgs[5].id],
337-
}
338-
):
339-
result = partition_by_measure(org_ids)
340-
341-
assert result[SamplingMeasure.SPANS] == sorted([orgs[2].id, orgs[7].id, orgs[5].id])
342-
expected_transaction_orgs = sorted(
343-
[org.id for org in orgs if org.id not in [orgs[2].id, orgs[7].id, orgs[5].id]]
344-
)
345-
assert result[SamplingMeasure.TRANSACTIONS] == expected_transaction_orgs
346-
347-
def test_partition_by_measure_returns_sorted_when_feature_disabled(self) -> None:
348-
org1 = self.create_organization("test-org1")
349-
org2 = self.create_organization("test-org2")
350-
org3 = self.create_organization("test-org3")
351-
352-
org_ids = [org3.id, org1.id, org2.id]
353-
354-
with self.options(
355-
{
356-
"dynamic-sampling.check_span_feature_flag": False,
357-
}
358-
):
359-
result = partition_by_measure(org_ids)
360-
361-
assert result[SamplingMeasure.TRANSACTIONS] == sorted(org_ids)
362-
assert SamplingMeasure.SPANS not in result

0 commit comments

Comments
 (0)