FEATURE: retry processing incoming emails on rate limit

This commit is contained in:
Régis Hanol 2016-08-08 22:28:27 +02:00
parent 5c06076b5c
commit 51322a46b3
5 changed files with 61 additions and 15 deletions

View file

@ -0,0 +1,16 @@
module Jobs
class ProcessEmail < Jobs::Base
sidekiq_options retry: 3
def execute(args)
Email::Processor.process!(args[:mail], false)
end
sidekiq_retries_exhausted do |msg|
Rails.logger.warn("Incoming email could not be processed after 3 retries.\n\n#{msg["args"][:mail]}")
end
end
end

View file

@ -2054,13 +2054,6 @@ en:
If you can correct the problem, please try again. If you can correct the problem, please try again.
email_reject_rate_limit_specified:
subject_template: "[%{site_name}] Email issue -- Rate limited"
text_body_template: |
We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work.
Reason: %{rate_limit_description}
email_reject_invalid_post_action: email_reject_invalid_post_action:
subject_template: "[%{site_name}] Email issue -- Invalid Post Action" subject_template: "[%{site_name}] Email issue -- Invalid Post Action"
text_body_template: | text_body_template: |

View file

@ -2,18 +2,21 @@ module Email
class Processor class Processor
def initialize(mail) def initialize(mail, retry_on_rate_limit=true)
@mail = mail @mail = mail
@retry_on_rate_limit = retry_on_rate_limit
end end
def self.process!(mail) def self.process!(mail, retry_on_rate_limit=true)
Email::Processor.new(mail).process! Email::Processor.new(mail, retry_on_rate_limit).process!
end end
def process! def process!
begin begin
receiver = Email::Receiver.new(@mail) receiver = Email::Receiver.new(@mail)
receiver.process! receiver.process!
rescue RateLimiter::LimitExceeded
@retry_on_rate_limit ? Jobs.enqueue(:process_email, mail: @mail) : raise
rescue Email::Receiver::BouncedEmailError => e rescue Email::Receiver::BouncedEmailError => e
# never reply to bounced emails # never reply to bounced emails
log_email_process_failure(@mail, e) log_email_process_failure(@mail, e)
@ -49,7 +52,6 @@ module Email
when ActiveRecord::Rollback then :email_reject_invalid_post when ActiveRecord::Rollback then :email_reject_invalid_post
when Email::Receiver::InvalidPostAction then :email_reject_invalid_post_action when Email::Receiver::InvalidPostAction then :email_reject_invalid_post_action
when Discourse::InvalidAccess then :email_reject_invalid_access when Discourse::InvalidAccess then :email_reject_invalid_access
when RateLimiter::LimitExceeded then :email_reject_rate_limit_specified
end end
template_args = {} template_args = {}
@ -61,10 +63,6 @@ module Email
template_args[:post_error] = e.message template_args[:post_error] = e.message
end end
if message_template == :email_reject_rate_limit_specified
template_args[:rate_limit_description] = e.description
end
if message_template if message_template
# inform the user about the rejection # inform the user about the rejection
message = Mail::Message.new(mail_string) message = Mail::Message.new(mail_string)

View file

@ -0,0 +1,27 @@
require "rails_helper"
require "email/processor"
describe Email::Processor do
describe "rate limits" do
let(:mail) { "From: foo@bar.com\nTo: bar@foo.com\nSubject: FOO BAR\n\nFoo foo bar bar?" }
let(:limit_exceeded) { RateLimiter::LimitExceeded.new(10) }
before do
Email::Receiver.any_instance.expects(:process!).raises(limit_exceeded)
end
it "enqueues a background job by default" do
Jobs.expects(:enqueue).with(:process_email, mail: mail)
Email::Processor.process!(mail)
end
it "doesn't enqueue a background job when retry is disabled" do
Jobs.expects(:enqueue).with(:process_email, mail: mail).never
expect { Email::Processor.process!(mail, false) }.to raise_error(limit_exceeded)
end
end
end

View file

@ -0,0 +1,12 @@
require "rails_helper"
describe Jobs::ProcessEmail do
let(:mail) { "From: foo@bar.com\nTo: bar@foo.com\nSubject: FOO BAR\n\nFoo foo bar bar?" }
it "process an email without retry" do
Email::Processor.expects(:process!).with(mail, false)
Jobs::ProcessEmail.new.execute(mail: mail)
end
end