Follow-ups to the per user work item update rate limit

What does this MR do and why?

The review of the per user work item update rate limit left three non-blocking items: the bulk limiter copied skip rules the rate limiter already owns, the oversized batch message read "1 items", and the Service Desk support bot shared one instance-wide bucket where a throttle would have left an issue closed with no visible error.

Resolving them gives the limit one source of truth for when it blocks, a correct message, and no silent failure for system-driven updates.

  • Add Gitlab::ApplicationRateLimiter.enforced_limit_for, which returns the limit or 0 when it would not block for the scope. It shares one private check with the throttle, and the bulk update and bulk move size checks use it instead of their own copies of the skip rules.
  • Pluralize the 422 message for an oversized bulk edit with n_().
  • Add a rate_limit_exempt? hook to RateLimitedService and override it in Issues::UpdateService for the support bot. This covers the Service Desk reopen note and ticket conversion with one exemption.

References

Screenshots or screen recordings

No UI changes.

How to set up and validate locally

bin/rspec spec/lib/gitlab/application_rate_limiter_spec.rb spec/services/work_items/bulk_update_rate_limiter_spec.rb spec/services/work_items/bulk_move_service_spec.rb spec/services/issues/update_service_spec.rb spec/lib/gitlab/email/handler/service_desk_handler_spec.rb
# => 0 failures
# Rails console
Gitlab::CurrentSettings.update!(work_item_update_limit: 1)
user = User.find_by_username('root')

Gitlab::ApplicationRateLimiter.enforced_limit_for(:work_item_update, scope: { user: user })
# => 0

Feature.enable(:work_item_update_rate_limit)
Gitlab::ApplicationRateLimiter.enforced_limit_for(:work_item_update, scope: { user: user })
# => 1

WorkItems::BulkUpdateRateLimiter.new(user: user, item_count: 2).check!
# => WorkItems::BulkUpdateRateLimiter::TooManyItemsError: No more than 1 item can be updated at the same time

project = Project.first
bot = Users::Internal.in_organization(project.organization_id).support_bot
Issues::UpdateService.new(container: project, current_user: bot, params: {}).send(:rate_limit_exempt?)
# => true

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 Daniyal Arshad

Merge request reports

Loading
Loading