Apply a note edit rate limit to REST, web and GraphQL

What does this MR do and why?

Editing a note has no rate limit, while creating one does, and every edit reprocesses mentions and cross references. This MR adds a notes_update rate limit of 300 requests per minute for each user. It has its own key, so editing and creating notes do not draw from the same budget, and it uses the notes_create_limit_allowlist so one list exempts a user from both limits.

It applies to every user-facing entry point that edits a note:

  • PUT .../notes/:note_id and PUT .../discussions/:discussion_id/notes/:note_id (REST)
  • PUT .../work_items/:work_item_iid/notes/:note_id (REST)
  • NotesActions#update (web note edit, including snippets)
  • the GraphQL updateNote, updateImageDiffNote and repositionImageDiffNote mutations

REST and GraphQL charge the limit before the note is looked up, like the create endpoints. The web action charges it after the user is authorized to edit the note. Requests over the limit return 429 Too Many Requests (REST and web) or the same "requested too many times" error that the create mutations return (GraphQL).

The limit ships behind the notes_update_rate_limit feature flag, which is disabled by default so that GitLab.com can enable it gradually and roll it back. The limit is listed with the non-configurable rate limits in the docs. The limit is fixed for now. Whether to make it an application setting depends on the decision in an open proposal on rate limiter endpoint grouping, so that is not part of this MR.

How to set up and validate locally

  1. Enable the feature flag, which is off by default:

    bundle exec rails runner 'Feature.enable(:notes_update_rate_limit)'
  2. Create a personal access token with the api scope and note one of your notes (project ID, issue IID, note ID).

  3. Send more edits than the limit within a minute:

    URL="http://gdk.test:8080/api/v4/projects/$PROJECT_ID/issues/$ISSUE_IID/notes/$NOTE_ID"
    
    # Send 310 edits, 40 at a time, then count the responses by HTTP status
    seq 1 310 | xargs -P 40 -I{} \
      curl -s -o /dev/null -w "%{http_code}\n" -X PUT \
      -H "PRIVATE-TOKEN: $TOKEN" --data-urlencode "body=edit {}" "$URL" |
      sort | uniq -c

    About 300 requests return 200 and the rest return 429.

  4. Add your username to the notes_create_limit_allowlist setting, wait a minute, and run the command again. Every request returns 200.

References

Relates to https://gitlab.com/gitlab-org/gitlab/-/work_items/634354

Rollout issue: #638521

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.

Edited by Sincheol (David) Kim

Merge request reports

Loading
Loading