Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Heroku timeout when sending emails

I am on Heroku with a custom domain, and I have the Redis add-on. I need help understanding how to create a background worker for email notifications. Users can inbox message each other, and I would like to send a email notification to the user for each new message received. I have the notifications working in development, but I am not good with creating background jobs which is required for Heroku, otherwise the server would timeout.

Messages Controller:

  def create
    @recipient = User.find(params[:user])
    current_user.send_message(@recipient, params[:body], params[:subject])
    flash[:notice] = "Message has been sent!"
    if request.xhr?
        render :json => {:notice => flash[:notice]}
    else
        redirect_to :conversations
    end
  end

User model:

def mailboxer_email(object)
    if self.no_email
      email
    else
        nil
    end
end

Mailboxer.rb:

Mailboxer.setup do |config|

  #Configures if you applications uses or no the email sending for Notifications and Messages
  config.uses_emails = false

  #Configures the default from for the email sent for Messages and Notifications of Mailboxer
  config.default_from = "[email protected]"

  #Configures the methods needed by mailboxer
  config.email_method = :mailboxer_email
  config.name_method = :name

  #Configures if you use or not a search engine and which one are you using
  #Supported enignes: [:solr,:sphinx]
  config.search_enabled = false
  config.search_engine = :sphinx
end
like image 852
xps15z Avatar asked Aug 31 '15 19:08

xps15z


People also ask

How do I fix Heroku H12 error?

A restart cancels long-running requests. Restarting can resolve H12 issues because freshly-booted dynos receive requests without interference from long-running requests. Using the Heroku CLI, run heroku ps:restart web to restart all web dynos. or, using the Heroku Dashboard, click More, then Restart all dynos.

Why is Heroku slow?

To preserve your dyno hours from burning out or wasting, Heroku puts your app to sleep after 30 minutes of inactivity. How generous! This is what makes your app is slow to load. When your app's dyno is asleep, waking it up may take some time.


2 Answers

Sidekiq is definitely the way to go with Heroku. I don't think mailboxer supports background configuration out of the box. Thankfully, it's still really easy with sidekiq's queueing process.

  1. Add gem 'sidekiq' to your gemfile and run bundle.
  2. Create a worker file app/workers/message_worker.rb.
class MessageWorker
  include Sidekiq::Worker

  def perform(sender_id, recipient_id, body, subject)
    sender = User.find(sender_id)
    recipient = User.find(recipient_id)
    sender.send_message(recipient, body, subject) 
  end
end
  1. Update your Controller to Queue Up the Worker

Remove: current_user.send_message(@recipient, params[:body], params[:subject])

Add: MessageWorker.perform_async(current_user.id, @recipient.id, params[:body], params[:subject])

Note: You should never pass workers ActiveRecord objects. That's why I setup this method to pass the User ids and look them up in the worker's perform method, instead of the entire object.

  1. Finally, restart your server and run bundle exec sidekiq. Now your app should be sending the email background.

  2. When you deploy, you will need a separate dyno for the worker which should look like this: worker: bundle exec sidekiq. You will also need Heroku's redis add-on.

like image 80
JeffD23 Avatar answered Sep 18 '22 14:09

JeffD23


Sounds like a H21 Request Timeout:

An HTTP request took longer than 30 seconds to complete.

To create a background worker for this in RoR, you should grab Resque, a Redis-backed background queueing library for RoR. Here is a demo. Another demo. And another demo.

To learn more about using Resque in Heroku, you can also read the herokue article up here. Or this tutorial (it's an old one though). Another great tutorial.

There is also a resque_mailer gem that will speed things up for you.

gem install resque_mailer #or add it to your Gemfile & use bundler 

It is fairly straightforward. Here is a snippet from a working demo by the author:

class Notifier < ActionMailer::Base
  include Resque::Mailer

  default :from => "[email protected]"

  def test(data={})
    data.symbolize_keys!

    Rails.logger.info "sending test mail"
    Rails.logger.info "params: #{data.keys.join(',')}"
    Rails.logger.info ""

    @subject = data[:subject] || "Testing mail"
    mail(:to => "[email protected]",
         :subject => @subject)
  end
end

doing Notifier.test.deliver will deliver the mail.

You can also consider using mail delivery services like SES.

like image 41
Archy Will He 何魏奇 Avatar answered Sep 17 '22 14:09

Archy Will He 何魏奇