How to send a large quantity of email in Rails, using ar_mailer

email-newsletterSending a large number of emails is not an easy task. It can take a lot of time so you need to do it asynchronously. Also, if you are on a shared hosting, you might be limited to a certain number of emails per hour, so you need to divide your list of email addresses in batches and send them at certain intervals.

There’s a plugin that can help us with this: ar_mailer.

This plugin adds a new deliver method to ActionMailer::Base :activerecord. In this delivery method, emails are not actually delivered, they are stored into a database table. A ruby script ar_sendmail can then be run (in a cron job) in order to send the emails saved in the database.

Step by step intructions on how to use the plugin:

1) Install the plugin

./script/plugin install git://github.com/adzap/ar_mailer.git

Add following line to config/environment.rb:

config.gem "adzap-ar_mailer", :source => 'http://gems.github.com'

2) Create table to store emails

./script/generate ar_mailer

This will create the Email model (and emails table) in which emails will be stored before sending.

3) Create mailer class

./script/generate mailer Newsletter

Add the following line to app/models/newsletter.rb:

self.delivery_method = :activerecord

You can now add in your controller the code to send emails (Newsletter.deliver_name_of_method). To test that everything is fine so far you can open your web app and perform an action that should send an email, then fire up the console and check emails table (you should see your email in Email.all).

You can also run ar_sendmail –mailq (in the project’s root directory) to check if the are emails in the queue (waiting the be sent).

4) Setup cron job to send emails in batches

We can run ar_sendmail as a daemon or put it in a cron job. The script will take emails from emails table and deliver them (after delivery emails will be deleted from the table). I prefer the second one. Here’s the command that you have to put in your cron file to send batches of 100 emails at intervals of 5 minutes:

*/5 * * * * cd /path-to-web-app/ && ar_sendmail -o --batch-size 100 --environment production >> log/cron.log 2>&1

This command also puts the output of ar_sendmail in log/cron.log.

It’s a good practice to redirect output from all your cron jobs to some log files (I use log/cron.log). This way you have details when something goes wrong.