Add per-user rate limits to job erasure

What does this MR do and why?

This MR adds per-user rate limits to job erasure in Ci::BuildEraseService. The limit check runs first, so a rejected call is still counted against the limit.

  • Per user and job: 5 requests a minute, fixed.
  • Per user and project: configurable (job_erase_limit_per_user_project), default 400 a minute, 0 disables it.

A blocked request gets a 429 with a Retry-After header from the REST API, and a 429 JSON response or a flash alert from the jobs controller. There is no GraphQL mutation for erasing a job. The limit is behind the rate_limit_job_erase feature flag, disabled by default.

References

Design notes

  • Ci::BuildEraseService is the only seam: both doors (REST and the jobs controller) already call it, and nothing else does.
  • Rejections at the door happen before the service runs, so they are not counted. For example, the REST endpoint's 403 for a non-erasable job and the controller's before_action permission check. This matches how the retry and play limits behave.
  • Ci::JobRateLimitable gains a default rate_limit? of true, so services without internal callers do not have to define it. The companion cancellation MR makes the same small change, so whichever merges second needs a rebase.

How to set up and validate locally

  1. In a rails console, enable the flag: Feature.enable(:rate_limit_job_erase).
  2. In Admin > Settings > CI/CD > Continuous Integration and Deployment, lower Maximum job erasures per project.
  3. Erase jobs from the UI, or call POST /api/v4/projects/:id/jobs/:job_id/erase, more times than the limit within a minute.
  4. Confirm you get a 429 with a Retry-After header from the API, or a flash alert from the UI.

MR acceptance checklist

Edited by Shabini Rajadas

Merge request reports

Loading
Loading