Rate limit Email on Push and Pipelines Email outbound emails

What does this MR do and why?

Adds a per-namespace outbound-email rate limit to the Email on Push and Pipelines Email integrations, extending the mechanism that already exists for Service Desk.

Both integrations accept a free-text list of external email addresses and send to all of them on every push / pipeline, with no send-side rate limit. This MR bounds recipients × events per root namespace so a single namespace cannot generate an unbounded volume of outbound email.

Implementation

  • Extracts the counting core of ServiceDesk::EmailRateLimiter into a reusable Gitlab::Email::NamespaceRateLimiter (namespace-scoped ApplicationRateLimiter, all-or-none batch suppression, structured log, 0 = unlimited, feature-flag gated). ServiceDesk::EmailRateLimiter now delegates to it and keeps its Service-Desk-only suppression notice.
  • Wires the limiter into EmailsOnPushWorker#perform (before the recipient loop) and Integrations::Base::PipelinesEmail#execute (before the worker enqueue). The shared NotificationService#pipeline_finished is deliberately left untouched, so member pipeline notifications are never throttled, only the arbitrary-external integration path is.
  • Separate rate-limiter buckets per feature, so one feature exhausting its quota never throttles another.
  • Charges per email sent, so the bound is on recipients × events, not events alone.
  • When an integration has no root namespace (instance/group-level), there is no namespace quota to charge, so sending falls through and the path is logged rather than being a silent bypass.

Design notes

  • Threshold values are read from the existing service_desk_outbound_emails_per_hour / _per_day plan_limit columns for all three features. This is deliberate: it avoids a migration and new plan_limits columns. The service_desk_* column is effectively the shared "outbound email" cap. Documented in-code as accepted debt; a follow-up can rename to a feature-neutral column if desired.
  • 0 = unlimited, so self-managed instances (which do not seed these plan limits) are unaffected.
  • No new plan_limits columns, no migration, no new seeds.
  • Recipient email addresses are never logged; the structured suppression log carries namespace / project IDs only.

Feature flags

Per-integration, actor = root namespace, type wip, default_enabled: false:

Flag Gates
emails_on_push_email_rate_limit Email on Push enforcement
pipelines_email_email_rate_limit Pipelines Email enforcement

Enforcement is off until the flag is enabled for a namespace, allowing a gradual per-namespace rollout.

Follow-ups

  • Possible future cleanup: rename the service_desk_outbound_emails_per_* plan_limit columns to a feature-neutral name now that they cap three features (accepted debt, documented in-code).

Test coverage

Over-limit suppresses the whole batch (all-or-none), under-limit sends, flag-off = no-op, 0 = unlimited = no-op, structured log emitted on suppression, nil-namespace path sends + logs, namespace isolation (sibling projects share quota, different namespaces isolated). Service Desk behavior is unchanged (regression specs green).

Edited by Kerri Miller

Merge request reports

Loading
Loading