diff options
Diffstat (limited to 'Bugzilla')
-rw-r--r-- | Bugzilla/Constants.pm | 18 | ||||
-rw-r--r-- | Bugzilla/Install/Requirements.pm | 3 | ||||
-rw-r--r-- | Bugzilla/Job/BugMail.pm | 16 | ||||
-rw-r--r-- | Bugzilla/Job/Mailer.pm | 22 | ||||
-rw-r--r-- | Bugzilla/Mailer.pm | 49 |
5 files changed, 89 insertions, 19 deletions
diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 37e995b81..6e0a9c7d3 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -208,6 +208,12 @@ use Memoize; AUDIT_CREATE AUDIT_REMOVE + + EMAIL_LIMIT_PER_MINUTE + EMAIL_LIMIT_PER_HOUR + EMAIL_LIMIT_EXCEPTION + + JOB_QUEUE_VIEW_MAX_JOBS ); @Bugzilla::Constants::EXPORT_OK = qw(contenttypes); @@ -632,6 +638,18 @@ use constant PRIVILEGES_REQUIRED_EMPOWERED => 3; use constant AUDIT_CREATE => '__create__'; use constant AUDIT_REMOVE => '__remove__'; +# The maximum number of emails per minute and hour a recipient can receive. +# Email will be queued/backlogged to avoid exceeeding these limits. +# Setting a limit to 0 will disable this feature. +use constant EMAIL_LIMIT_PER_MINUTE => 1000; +use constant EMAIL_LIMIT_PER_HOUR => 2500; +# Don't change this exception message. +use constant EMAIL_LIMIT_EXCEPTION => "email_limit_exceeded\n"; + +# The maximum number of jobs to show when viewing the job queue +# (view_job_queue.cgi). +use constant JOB_QUEUE_VIEW_MAX_JOBS => 2500; + sub bz_locations { # Force memoize() to re-compute data per project, to avoid # sharing the same data across different installations. diff --git a/Bugzilla/Install/Requirements.pm b/Bugzilla/Install/Requirements.pm index 1c9c91345..6bb012230 100644 --- a/Bugzilla/Install/Requirements.pm +++ b/Bugzilla/Install/Requirements.pm @@ -348,7 +348,8 @@ sub OPTIONAL_MODULES { { package => 'TheSchwartz', module => 'TheSchwartz', - version => 0, + # 1.10 supports declining of jobs. + version => 1.10, feature => ['jobqueue'], }, { diff --git a/Bugzilla/Job/BugMail.pm b/Bugzilla/Job/BugMail.pm index 9c176b005..403d936ad 100644 --- a/Bugzilla/Job/BugMail.pm +++ b/Bugzilla/Job/BugMail.pm @@ -13,19 +13,9 @@ use strict; use Bugzilla::BugMail; BEGIN { eval "use parent qw(Bugzilla::Job::Mailer)"; } -sub work { - my ($class, $job) = @_; - my $success = eval { - Bugzilla::BugMail::dequeue($job->arg->{vars}); - 1; - }; - if (!$success) { - $job->failed($@); - undef $@; - } - else { - $job->completed; - } +sub process_job { + my ($class, $arg) = @_; + Bugzilla::BugMail::dequeue($arg->{vars}); } 1; diff --git a/Bugzilla/Job/Mailer.pm b/Bugzilla/Job/Mailer.pm index 09c387326..e3b94894a 100644 --- a/Bugzilla/Job/Mailer.pm +++ b/Bugzilla/Job/Mailer.pm @@ -22,6 +22,9 @@ package Bugzilla::Job::Mailer; use strict; +use warnings; + +use Bugzilla::Constants; use Bugzilla::Mailer; BEGIN { eval "use base qw(TheSchwartz::Worker)"; } @@ -43,15 +46,24 @@ sub retry_delay { sub work { my ($class, $job) = @_; - my $msg = $job->arg->{msg}; - my $success = eval { MessageToMTA($msg, 1); 1; }; - if (!$success) { - $job->failed($@); + eval { $class->process_job($job->arg) }; + if (my $error = $@) { + if ($error eq EMAIL_LIMIT_EXCEPTION) { + $job->declined(); + } + else { + $job->failed($error); + } undef $@; - } + } else { $job->completed; } } +sub process_job { + my ($class, $arg) = @_; + MessageToMTA($arg, 1); +} + 1; diff --git a/Bugzilla/Mailer.pm b/Bugzilla/Mailer.pm index 381422821..556500b1a 100644 --- a/Bugzilla/Mailer.pm +++ b/Bugzilla/Mailer.pm @@ -67,6 +67,8 @@ sub MessageToMTA { return; } + my $dbh = Bugzilla->dbh; + my $email; if (ref $msg) { $email = $msg; @@ -82,6 +84,42 @@ sub MessageToMTA { $email = new Email::MIME($msg); } + # Ensure that we are not sending emails too quickly to recipients. + if (Bugzilla->params->{use_mailer_queue} + && (EMAIL_LIMIT_PER_MINUTE || EMAIL_LIMIT_PER_HOUR)) + { + $dbh->do( + "DELETE FROM email_rates WHERE message_ts < " + . $dbh->sql_date_math('LOCALTIMESTAMP(0)', '-', '1', 'HOUR')); + + my $recipient = $email->header('To'); + + if (EMAIL_LIMIT_PER_MINUTE) { + my $minute_rate = $dbh->selectrow_array( + "SELECT COUNT(*) + FROM email_rates + WHERE recipient = ? AND message_ts >= " + . $dbh->sql_date_math('LOCALTIMESTAMP(0)', '-', '1', 'MINUTE'), + undef, + $recipient); + if ($minute_rate >= EMAIL_LIMIT_PER_MINUTE) { + die EMAIL_LIMIT_EXCEPTION; + } + } + if (EMAIL_LIMIT_PER_HOUR) { + my $hour_rate = $dbh->selectrow_array( + "SELECT COUNT(*) + FROM email_rates + WHERE recipient = ? AND message_ts >= " + . $dbh->sql_date_math('LOCALTIMESTAMP(0)', '-', '1', 'HOUR'), + undef, + $recipient); + if ($hour_rate >= EMAIL_LIMIT_PER_HOUR) { + die EMAIL_LIMIT_EXCEPTION; + } + } + } + # We add this header to uniquely identify all email that we # send as coming from this Bugzilla installation. # @@ -208,6 +246,17 @@ sub MessageToMTA { ThrowCodeError('mail_send_error', { msg => $retval, mail => $email }) if !$retval; } + + # insert into email_rates + if (Bugzilla->params->{use_mailer_queue} + && (EMAIL_LIMIT_PER_MINUTE || EMAIL_LIMIT_PER_HOUR)) + { + $dbh->do( + "INSERT INTO email_rates(recipient, message_ts) VALUES (?, LOCALTIMESTAMP(0))", + undef, + $email->header('To') + ); + } } # Builds header suitable for use as a threading marker in email notifications |