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::EmailRateLimiterinto a reusableGitlab::Email::NamespaceRateLimiter(namespace-scopedApplicationRateLimiter, all-or-none batch suppression, structured log,0 = unlimited, feature-flag gated).ServiceDesk::EmailRateLimiternow delegates to it and keeps its Service-Desk-only suppression notice. - Wires the limiter into
EmailsOnPushWorker#perform(before the recipient loop) andIntegrations::Base::PipelinesEmail#execute(before the worker enqueue). The sharedNotificationService#pipeline_finishedis 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_dayplan_limit columns for all three features. This is deliberate: it avoids a migration and newplan_limitscolumns. Theservice_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_limitscolumns, 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).