Rate limit the metric definitions export per IP
What does this MR do and why?
GET /api/v4/usage_data/metric_definitions is public by design: it's documented as unauthenticated, and its spec asserts 200 without a token. Each response is about 3.8 MB of uncompressed YAML. On GitLab.com, nearly every request lands on a Puma worker that hasn't built the YAML yet, which costs about 0.85s of CPU. Nothing limits how often a client can call it.
This MR adds an ApplicationRateLimiter limit of 30 requests per minute per IP address (metric_definitions_download). Authenticated requests are counted per IP too, because they cost the same.
The rule is a staged_rule. Until the rate_limit_metric_definitions feature flag is enabled, requests over the limit are logged (rate_limit_state ends in limit_metric_definition_downloads_by_ip_log:log) but not blocked. When the flag is on, they get 429 with Retry-After. The limit is checked before the YAML is built, so blocked requests skip that cost.
- Feature flag:
gitlab_com_derisk, with an instance actor, as forpages_deployments_api_unauthenticated. Rollout issue: #639898 - Limit: 30 is the top of the 10–30 range discussed in the issue, because GitLab.com CI runners share egress IPs. Log mode will show whether real callers come close.
- Docs: a
429entry in both OpenAPI files, a row in the non-configurable limits page, and a pointer from the API page.
This is the per-IP limit from the issue's suggested path. Edge caching and compression for YAML are separate follow-ups with Infrastructure.
References
- https://gitlab.com/gitlab-org/gitlab/-/work_items/635595
- Agreed approach: https://gitlab.com/gitlab-org/gitlab/-/work_items/635595#note_4221384764
- Rollout issue: #639898
Screenshots or screen recordings
Not applicable: no UI changes.
How to set up and validate locally
-
With the flag disabled (the default), request the endpoint 31 times. Every request returns
200:for i in $(seq 31); do curl --silent --output /dev/null --write-out '%{http_code}\n' "http://gdk.test:3000/api/v4/usage_data/metric_definitions"; doneIn
log/api_json.log, the 31st request has arate_limit_stateentry ending inlimit_metric_definition_downloads_by_ip_log:log. -
In the Rails console, enable the flag:
Feature.enable(:rate_limit_metric_definitions) -
Wait a minute for the window to reset, then repeat the loop. The 31st request returns
429with aRetry-Afterheader.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.