Return Retry-After header when rate limit is hit
What does this MR do and why?
This MR improves the API rate limiting behavior by returning a proper Retry-After header when rate limits are hit. Previously, the API::Helpers::RateLimiter#check_rate_limit! method was calling render_api_error! directly, missing the opportunity to provide clients with timing information about when they can retry their requests.
Changes made:
- Replace
render_api_error!withtoo_many_requests!helper method in rate limiter - Add
Retry-Afterheader with the configured rate limit interval - Make
Gitlab::ApplicationRateLimiter.intervalmethod public to support this functionality - Add comprehensive test coverage for the new behavior
Why this matters:
- Clients like GitLab Runner can now know exactly how long to wait before retrying
- Reduces unnecessary retry attempts that would immediately hit the rate limit again
- Follows HTTP standards for rate limiting (RFC 6585)
- Improves overall API user experience
Changelog: fixed
References
- Closes #572559 (closed)
- Related to gitlab-com/gl-infra/production-engineering#25372
Screenshots or screen recordings
This is a backend API change with no UI impact. The improvement is visible in HTTP response headers:
Before:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{"message": "This endpoint has been requested too many times. Try again later."}
After:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 600
{"message": "This endpoint has been requested too many times. Try again later."}
How to set up and validate locally
-
Set up a rate limit for testing in Rails console:
# Enable a low rate limit for testing stub_application_setting(throttle_authenticated_api_enabled: true) stub_application_setting(throttle_authenticated_api_requests_per_period: 1) stub_application_setting(throttle_authenticated_api_period_in_seconds: 60) -
Make API requests to trigger the rate limit:
# First request should succeed curl -H "Authorization: Bearer YOUR_TOKEN" "http://localhost:3000/api/v4/projects" # Second request should return 429 with Retry-After header curl -I -H "Authorization: Bearer YOUR_TOKEN" "http://localhost:3000/api/v4/projects" -
Verify the
Retry-Afterheader is present in the 429 response -
Run the new integration tests:
bundle exec rspec spec/lib/api/helpers/rate_limiter_integration_spec.rb bundle exec rspec spec/lib/api/helpers/rate_limiter_spec.rb
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.
-
Functionality: Improves API rate limiting by providing proper Retry-Afterheaders -
Performance: No performance impact - only changes response headers -
Reliability: Comprehensive test coverage added for new functionality -
Security: No security implications - maintains existing rate limiting behavior -
Maintainability: Code is cleaner by reusing existing too_many_requests!helper -
Documentation: No documentation changes needed - this is an HTTP standard header -
Testing: Added integration tests and updated unit tests