Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ddm): Implement global abuse limits for metrics #64574

Merged
merged 10 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/sentry/options/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,14 @@
flags=FLAG_PRIORITIZE_DISK | FLAG_AUTOMATOR_MODIFIABLE,
)


register(
"global-abuse-quota.metric-bucket-limit",
type=Int,
default=0,
flags=FLAG_PRIORITIZE_DISK | FLAG_AUTOMATOR_MODIFIABLE,
)

# END ABUSE QUOTAS

# Send event messages for specific project IDs to random partitions in Kafka
Expand Down
10 changes: 9 additions & 1 deletion src/sentry/quotas/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class QuotaScope(IntEnum):
ORGANIZATION = 1
PROJECT = 2
KEY = 3
GLOBAL = 4

def api_name(self):
return self.name.lower()
Expand All @@ -36,7 +37,7 @@ class AbuseQuota:
# Quota categories.
categories: list[DataCategory]
# Quota Scope.
scope: Literal[QuotaScope.ORGANIZATION, QuotaScope.PROJECT]
scope: Literal[QuotaScope.ORGANIZATION, QuotaScope.PROJECT, QuotaScope.GLOBAL]
# Old org option name still used for compatibility reasons,
# takes precedence over `option` and `compat_option_sentry`.
compat_option_org: str | None = None
Expand Down Expand Up @@ -404,6 +405,12 @@ def get_abuse_quotas(self, org):
categories=[DataCategory.METRIC_BUCKET],
scope=QuotaScope.ORGANIZATION,
),
AbuseQuota(
id="gam",
option="global-abuse-quota.metric-bucket-limit",
categories=[DataCategory.METRIC_BUCKET],
scope=QuotaScope.GLOBAL,
),
]

# XXX: These reason codes are hardcoded in getsentry:
Expand All @@ -412,6 +419,7 @@ def get_abuse_quotas(self, org):
reason_codes = {
QuotaScope.ORGANIZATION: "org_abuse_limit",
QuotaScope.PROJECT: "project_abuse_limit",
QuotaScope.GLOBAL: "global_abuse_limit",
}

for quota in abuse_quotas:
Expand Down
8 changes: 8 additions & 0 deletions tests/sentry/quotas/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ def test_check_accept_monitor_checkin(self):
"reasonCode": "go_away",
},
),
(
QuotaConfig(limit=0, scope=QuotaScope.GLOBAL, reason_code="come back!"),
{
"limit": 0,
"scope": "global",
"reasonCode": "come back!",
},
),
],
)
def test_quotas_to_json(obj, json):
Expand Down
15 changes: 15 additions & 0 deletions tests/sentry/quotas/test_redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ class RedisQuotaTest(TestCase):
def quota(self):
return RedisQuota()

def test_redis_quota_serialize(self):
assert QuotaScope.ORGANIZATION.api_name() == "organization"
assert QuotaScope.PROJECT.api_name() == "project"
assert QuotaScope.KEY.api_name() == "key"
assert QuotaScope.GLOBAL.api_name() == "global"

def test_abuse_quotas(self):
# These legacy options need to be set, otherwise we'll run into
# AssertionError: reject-all quotas cannot be tracked
Expand Down Expand Up @@ -113,6 +119,7 @@ def test_abuse_quotas(self):
self.organization.update_option("project-abuse-quota.attachment-limit", 601)
self.organization.update_option("project-abuse-quota.session-limit", 602)
self.organization.update_option("organization-abuse-quota.metric-bucket-limit", 603)
self.organization.update_option("global-abuse-quota.metric-bucket-limit", 604)
with self.feature("organizations:transaction-metrics-extraction"):
quotas = self.quota.get_quotas(self.project)

Expand Down Expand Up @@ -148,6 +155,14 @@ def test_abuse_quotas(self):
assert quotas[4].window == 10
assert quotas[4].reason_code == "org_abuse_limit"

assert quotas[5].id == "gam"
assert quotas[5].scope == QuotaScope.GLOBAL
assert quotas[5].scope_id is None
assert quotas[5].categories == {DataCategory.METRIC_BUCKET}
assert quotas[5].limit == 6040
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is this number coming from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above here I set the limit to 604.

self.organization.update_option("global-abuse-quota.metric-bucket-limit", 604)

since the abuse_window is 10 seconds its multiplied by 10

assert quotas[5].window == 10
assert quotas[5].reason_code == "global_abuse_limit"

# Let's set the global option for error limits.
# Since we already have an org override for it, it shouldn't change anything.
with self.options({"project-abuse-quota.error-limit": 3}):
Expand Down
Loading