Rate limit moving and cloning issues per user
What does this MR do and why?
Moving or cloning an issue copies its whole history inside the request, and the only limit on that path is issues_create, keyed on the target project, so it never bounds one user. This MR adds a rate limit of 30 moves and clones per minute per user. The limit is the work_item_move_limit application setting, labeled beta in the docs, following https://gitlab.com/groups/gitlab-org/-/work_items/24176#note_4152547896. The key is a staged_rule, so it starts in log mode: requests are counted on limit_work_item_moves_by_user_log and recorded in rate_limit_state, not blocked, until the work_item_move_rate_limit_enforce feature flag (disabled by default) is enabled.
The check is the rate_limit DSL on WorkItems::DataSync::MoveService and CloneService, so one key covers every entry point:
POST .../issues/:issue_iid/moveandPOST .../issues/:issue_iid/clone(REST)- the
issueMoveandworkItemBulkMoveGraphQL mutations - the issue page, the
/moveand/clonequick actions and the Slackissue movecommand
A bulk move is charged one request per item and is checked once before any item moves, so a throttled batch is rejected whole. Once the limit is enforced, a batch with more items than the limit is rejected up front with an error, before anything is charged; in log mode it is charged and logged like any other batch. Users on the throttle allowlist are exempt from both, as they are from every application rate limit. Child items moved along with an issue are debited the same way after the parent move was admitted, so the user's next move is throttled instead of a move failing halfway. Requests the user cannot perform also count, as in the label delete limit. Requests over the limit return 429 Too Many Requests; a throttled /move or /clone quick action in an issue update is reported after the other changes in that update were saved.
The threshold comes from production data in the confidential issue. Capping the notes copied per call is a separate follow-up.
References
- Feature flag rollout issue: #638511
How to set up and validate locally
-
Lower the limit through the application settings API:
curl --request PUT --header "PRIVATE-TOKEN: <admin token>" "http://gdk.test:3000/api/v4/application/settings?work_item_move_limit=2" -
With the flag still disabled, move three issues to another project within a minute. All three succeed, and the third request's line in
log/api_json.logcarriesrate_limit_statewithapplimiter_work_item_move:limit_work_item_moves_by_user_log:log. -
Enable the feature flag in the Rails console:
Feature.enable(:work_item_move_rate_limit_enforce) -
Move two issues to another project within a minute. Both succeed.
-
Clone a third issue within the minute. The request returns 429, because the key is shared.
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.