Snowman
Snowman

Reputation: 32091

deliver_later with Sidekiq causes 500 error when unable to connect to Redis

In one of my Rails application controllers, I do something like this:

def create
  @user = User.create(user_params)

  # send welcome email
  UserMailer.welcome(@user).deliver_later
end

Now, I've intentionally disabled my Redis server so that I can replicate what would happen in the case that a connection couldn't be made from my app.

Unfortunately, this entire create request fails with a 500 if the deliver_later is unable to connect to Redis.

What I'd like is that the request still succeeds, but the mailer fails silently.

How can I accomplish this?

Additional information:

In config/initializers/action_mailer.rb:

  rescue_from(Redis::CannotConnectError) do |exception|
      Rails.logger.error "Original record not found: #{@serialized_arguments.join(', ')}"
  end

This never gets called though on exception. I tried rescue_from(StandardError) and (Exception), but that was never called either.

I'm using sidekiq as my job queue adapter:

config.active_job.queue_adapter = :sidekiq

The 500 error I get is:

Redis::CannotConnectError (Error connecting to Redis on localhost:6379 (Errno::ECONNREFUSED)):

My UserMailer is a subclass of ApplicationMailer which is a subclass of ActionMailer::Base.

Upvotes: 2

Views: 616

Answers (1)

gmcnaughton
gmcnaughton

Reputation: 2293

In order to prevent calls to deliver_later from crashing when Redis is down, we added the following monkey-patch:

# If +raise_delivery_errors+ is false, errors occurring while attempting to
# enqueue the email for delivery will be ignored (for instance, if Redis is
# unreachable). In these cases, the email will never be delivered.
module MessageDeliveryWithSilentQueueingErrors
  def deliver_later
    super
  rescue Redis::CannotConnectError => e
    raise if raise_delivery_errors
    # Log details of the failed email here
  end

  def deliver_later!
    super
  rescue Redis::CannotConnectError => e
    raise if raise_delivery_errors
    # Log details of the failed email here
  end
end

ActionMailer::MessageDelivery.send(:prepend, MessageDeliveryWithSilentQueueingErrors)

Upvotes: 1

Related Questions