Skip to content

Commit 94904f8

Browse files
authored
fix(dynamic-sampling): fix audit logging for biases (#95427)
1 parent 483406b commit 94904f8

File tree

2 files changed

+127
-4
lines changed

2 files changed

+127
-4
lines changed

src/sentry/api/endpoints/project_details.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,15 +1024,20 @@ def dynamic_sampling_biases_audit_log(
10241024
if old_raw_dynamic_sampling_biases is None:
10251025
return
10261026

1027-
for index, rule in enumerate(new_raw_dynamic_sampling_biases):
1028-
if rule["active"] != old_raw_dynamic_sampling_biases[index]["active"]:
1027+
old_biases = {bias["id"]: bias for bias in old_raw_dynamic_sampling_biases}
1028+
new_biases = {bias["id"]: bias for bias in new_raw_dynamic_sampling_biases}
1029+
for bias_id, new_bias in new_biases.items():
1030+
old_bias = old_biases.get(bias_id)
1031+
if (old_bias is None and new_bias["active"]) or (
1032+
old_bias is not None and new_bias["active"] != old_bias["active"]
1033+
):
10291034
self.create_audit_entry(
10301035
request=request,
10311036
organization=project.organization,
10321037
target_object=project.id,
10331038
event=audit_log.get_event_id(
1034-
"SAMPLING_BIAS_ENABLED" if rule["active"] else "SAMPLING_BIAS_DISABLED"
1039+
"SAMPLING_BIAS_ENABLED" if new_bias["active"] else "SAMPLING_BIAS_DISABLED"
10351040
),
1036-
data={**project.get_audit_log_data(), "name": rule["id"]},
1041+
data={**project.get_audit_log_data(), "name": bias_id},
10371042
)
10381043
return

tests/sentry/api/endpoints/test_project_details.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1868,6 +1868,124 @@ def test_put_new_dynamic_sampling_incorrect_rules_with_correct_flags(self):
18681868
"Error: Only 'id' and 'active' fields are allowed for bias."
18691869
]
18701870

1871+
def test_dynamic_sampling_bias_enable_audit_log(self):
1872+
"""Test that enabling a dynamic sampling bias creates the correct audit log entry."""
1873+
with self.feature("organizations:dynamic-sampling"):
1874+
# Start with default biases
1875+
with outbox_runner():
1876+
self.get_success_response(
1877+
self.org_slug, self.proj_slug, dynamicSamplingBiases=DEFAULT_BIASES
1878+
)
1879+
1880+
# Enable a specific bias
1881+
updated_biases = [
1882+
{"id": RuleType.MINIMUM_SAMPLE_RATE_RULE.value, "active": True},
1883+
]
1884+
self.get_success_response(
1885+
self.org_slug, self.proj_slug, dynamicSamplingBiases=updated_biases
1886+
)
1887+
1888+
# Check audit log entry was created
1889+
with assume_test_silo_mode(SiloMode.CONTROL):
1890+
audit_entry = AuditLogEntry.objects.get(
1891+
organization_id=self.organization.id,
1892+
event=audit_log.get_event_id("SAMPLING_BIAS_ENABLED"),
1893+
target_object=self.project.id,
1894+
)
1895+
assert audit_entry is not None
1896+
assert audit_entry.data["name"] == RuleType.MINIMUM_SAMPLE_RATE_RULE.value
1897+
1898+
def test_dynamic_sampling_bias_disable_audit_log(self):
1899+
"""Test that disabling a dynamic sampling bias creates the correct audit log entry."""
1900+
with self.feature("organizations:dynamic-sampling"):
1901+
# Start with a bias enabled
1902+
with outbox_runner():
1903+
initial_biases = [{"id": RuleType.BOOST_ENVIRONMENTS_RULE.value, "active": True}]
1904+
self.get_success_response(
1905+
self.org_slug, self.proj_slug, dynamicSamplingBiases=initial_biases
1906+
)
1907+
1908+
# Disable the bias
1909+
disabled_biases = [{"id": RuleType.BOOST_ENVIRONMENTS_RULE.value, "active": False}]
1910+
self.get_success_response(
1911+
self.org_slug, self.proj_slug, dynamicSamplingBiases=disabled_biases
1912+
)
1913+
1914+
# Check audit log entry was created
1915+
with assume_test_silo_mode(SiloMode.CONTROL):
1916+
audit_entry = AuditLogEntry.objects.get(
1917+
organization_id=self.organization.id,
1918+
event=audit_log.get_event_id("SAMPLING_BIAS_DISABLED"),
1919+
target_object=self.project.id,
1920+
)
1921+
assert audit_entry is not None
1922+
assert audit_entry.data["name"] == RuleType.BOOST_ENVIRONMENTS_RULE.value
1923+
1924+
def test_dynamic_sampling_bias_add_new_bias_audit_log(self):
1925+
"""Test that adding a new bias to existing biases creates the correct audit log entry."""
1926+
with self.feature("organizations:dynamic-sampling"):
1927+
# Start with some initial biases
1928+
initial_biases = [
1929+
{"id": RuleType.BOOST_ENVIRONMENTS_RULE.value, "active": False},
1930+
{"id": RuleType.BOOST_LATEST_RELEASES_RULE.value, "active": False},
1931+
]
1932+
with outbox_runner():
1933+
self.get_success_response(
1934+
self.org_slug, self.proj_slug, dynamicSamplingBiases=initial_biases
1935+
)
1936+
1937+
# Add a new bias that's enabled
1938+
expanded_biases = [
1939+
{"id": RuleType.BOOST_ENVIRONMENTS_RULE.value, "active": False},
1940+
{"id": RuleType.BOOST_LATEST_RELEASES_RULE.value, "active": False},
1941+
{"id": RuleType.MINIMUM_SAMPLE_RATE_RULE.value, "active": True},
1942+
]
1943+
self.get_success_response(
1944+
self.org_slug, self.proj_slug, dynamicSamplingBiases=expanded_biases
1945+
)
1946+
1947+
# Check audit log entry was created for the newly enabled bias
1948+
with assume_test_silo_mode(SiloMode.CONTROL):
1949+
audit_entry = AuditLogEntry.objects.get(
1950+
organization_id=self.organization.id,
1951+
event=audit_log.get_event_id("SAMPLING_BIAS_ENABLED"),
1952+
target_object=self.project.id,
1953+
)
1954+
assert audit_entry is not None
1955+
assert audit_entry.data["name"] == RuleType.MINIMUM_SAMPLE_RATE_RULE.value
1956+
1957+
def test_dynamic_sampling_bias_add_new_inactive_bias_no_audit_log(self):
1958+
"""Test that adding a new bias as inactive does not create an audit log entry."""
1959+
with self.feature("organizations:dynamic-sampling"):
1960+
# Start with some initial biases
1961+
initial_biases = [
1962+
{"id": RuleType.BOOST_ENVIRONMENTS_RULE.value, "active": False},
1963+
{"id": RuleType.BOOST_LATEST_RELEASES_RULE.value, "active": False},
1964+
]
1965+
with outbox_runner():
1966+
self.get_success_response(
1967+
self.org_slug, self.proj_slug, dynamicSamplingBiases=initial_biases
1968+
)
1969+
1970+
# Add a new bias that's inactive
1971+
expanded_biases = [
1972+
{"id": RuleType.BOOST_ENVIRONMENTS_RULE.value, "active": False},
1973+
{"id": RuleType.BOOST_LATEST_RELEASES_RULE.value, "active": False},
1974+
{"id": RuleType.MINIMUM_SAMPLE_RATE_RULE.value, "active": False},
1975+
]
1976+
self.get_success_response(
1977+
self.org_slug, self.proj_slug, dynamicSamplingBiases=expanded_biases
1978+
)
1979+
1980+
# Check that no audit log entry was created for the inactive bias
1981+
with assume_test_silo_mode(SiloMode.CONTROL):
1982+
audit_entries = AuditLogEntry.objects.filter(
1983+
organization_id=self.organization.id,
1984+
event=audit_log.get_event_id("SAMPLING_BIAS_ENABLED"),
1985+
target_object=self.project.id,
1986+
)
1987+
assert audit_entries.count() == 0
1988+
18711989

18721990
class TestTempestProjectDetails(TestProjectDetailsBase):
18731991
endpoint = "sentry-api-0-project-details"

0 commit comments

Comments
 (0)