Skip to content

Commit ea5a84a

Browse files
authored
chore(autofix): Dynamic auto-autofix rate limit (#94256)
Increases the rate limit for auto-triggered Autofix as your automation setting becomes more strict. The goal of this logic is to enable high usage for large orgs with reasonable settings, while protecting small orgs with extreme settings from surprise bills.
1 parent 9520960 commit ea5a84a

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

src/sentry/autofix/utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,17 @@ def is_seer_scanner_rate_limited(
186186
return is_rate_limited, current, limit
187187

188188

189+
AUTOFIX_AUTOTRIGGED_RATE_LIMIT_OPTION_MULTIPLIERS = {
190+
"off": 5,
191+
"super_low": 5,
192+
"low": 4,
193+
"medium": 3,
194+
"high": 2,
195+
"always": 1,
196+
None: 1, # default if option is not set
197+
}
198+
199+
189200
def is_seer_autotriggered_autofix_rate_limited(
190201
project: Project, organization: Organization
191202
) -> tuple[bool, int, int]:
@@ -202,6 +213,14 @@ def is_seer_autotriggered_autofix_rate_limited(
202213
return False, 0, 0
203214

204215
limit = options.get("seer.max_num_autofix_autotriggered_per_hour", 20)
216+
217+
# The more selective automation is, the higher the limit we allow.
218+
# This is to protect projects with extreme settings from starting too many runs
219+
# while allowing big projects with reasonable settings to run more often.
220+
option = project.get_option("sentry:autofix_automation_tuning")
221+
multiplier = AUTOFIX_AUTOTRIGGED_RATE_LIMIT_OPTION_MULTIPLIERS.get(option, 1)
222+
limit *= multiplier
223+
205224
is_rate_limited, current, _ = ratelimits.backend.is_limited_with_value(
206225
project=project,
207226
key="autofix.auto_triggered",

tests/sentry/autofix/test_utils.py

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

77
from sentry.autofix.utils import (
8+
AUTOFIX_AUTOTRIGGED_RATE_LIMIT_OPTION_MULTIPLIERS,
89
AutofixState,
910
AutofixStatus,
1011
get_autofix_repos_from_project_code_mappings,
@@ -236,6 +237,8 @@ def test_autofix_rate_limited_logic(self, mock_is_limited):
236237
project = self.create_project()
237238
organization = project.organization
238239

240+
project.update_option("sentry:autofix_automation_tuning", None)
241+
239242
mock_is_limited.return_value = (True, 19, None)
240243

241244
with self.options({"seer.max_num_autofix_autotriggered_per_hour": 20}):
@@ -246,3 +249,24 @@ def test_autofix_rate_limited_logic(self, mock_is_limited):
246249
assert is_rate_limited is True
247250
assert current == 19
248251
assert limit == 20
252+
253+
@patch("sentry.autofix.utils.ratelimits.backend.is_limited_with_value")
254+
def test_autofix_rate_limit_multiplication_logic(self, mock_is_limited):
255+
"""Test that the limit is multiplied correctly based on project option"""
256+
project = self.create_project()
257+
organization = project.organization
258+
mock_is_limited.return_value = (False, 0, None)
259+
260+
base_limit = 20
261+
262+
for option, multiplier in AUTOFIX_AUTOTRIGGED_RATE_LIMIT_OPTION_MULTIPLIERS.items():
263+
with self.options({"seer.max_num_autofix_autotriggered_per_hour": base_limit}):
264+
project.update_option("sentry:autofix_automation_tuning", option)
265+
is_seer_autotriggered_autofix_rate_limited(project, organization)
266+
expected_limit = base_limit * multiplier
267+
mock_is_limited.assert_called_with(
268+
project=project,
269+
key="autofix.auto_triggered",
270+
limit=expected_limit,
271+
window=60 * 60,
272+
)

0 commit comments

Comments
 (0)