preloader
blog post

Making Devise Emails More Reliable with Mailsnag and ActiveJob

Last week, we came across a fantastic article on the AppSignal blog that introduced us to devise, a gem for Ruby on Rails that simplifies user authentication. Inspired by this article, we began wondering if there was a way to enhance devise’s reliability when sending emails.

We decided to connect devise with Mailsnag, a tool that helps us catch potential issues with our email delivery. The results were remarkable! By using Mailsnag, we were able to identify and resolve a few edge cases that could have caused frustration for our users. In this article, we’ll guide you through the process of using Mailsnag to identify and handle similar issues gracefully in your app.

Getting Started

We followed the AppSignal blog and created a tasks_app with devise integration.

Link to the changes

Connect to Mailsnag

Once we had our app set up, we created a new mailbox on Mailsnag and connected it to our application by configuring SMTP for ActionMailer in the development environment.

Important: During the mailbox setup on Mailsnag, we checked the Simulate Errors checkbox. This setting allowed Mailsnag to simulate intermittent errors, revealing any potential issues in our SMTP integration (in this case, with devise).

To enable Mailsnag as our SMTP server, we made the following changes to our config/environments/development.rb file:

config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  user_name: ENV.fetch("SMTP_USER"),
  password: ENV.fetch("SMTP_PASSWORD"),
  address: "smtp.mailsnag.com",
  port: 2525,
  authentication: "plain",
  enable_starttls_auto: true
}

Rails.application.routes.default_url_options[:host] = "localhost:3000"

The code above will let our app use Mailsnag SMTP server for sending emails in development environment. It will also set default host for ActionMailer to localhost:3000 so that devise can generate correct links for confirmation emails.

Additionally, we added various devise modules such as :confirmable, :lockable, :timeoutable, and :trackable to our User model for enhanced functionality:

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :confirmable, :lockable, :timeoutable, :trackable

To accommodate these changes, we also made corresponding adjustments in the DeviseCreateUsers migration file and performed the necessary database migration:

# Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip

# Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable

# Lockable
t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
t.string :unlock_token # Only if unlock strategy is :email or :both
t.datetime :locked_at

Link to the changes

Testing Our Setup

With everything set up, we decided to test the registration process by signing up a new user. However, due to the Simulate Errors feature in Mailsnag, we encountered an intermittent error when trying to send the confirmation email. This error can mimic real-world scenarios where network connectivity issues or SMTP service problems may occur.

Unfortunately, devise couldn’t handle this error gracefully. As a result, the user faced confusion and frustration. Moreover, attempting to sign up again led to another error: Email has already been taken. This happened because devise saved the new user in the database but failed to send the confirmation email. Clearly, we needed a solution that would handle SMTP outages more efficiently and allow for smooth retries in the background.

Making Devise More Reliable

To improve the reliability of our application and make devise more resilient, we implemented a solution using ActiveJob. By delivering emails asynchronously in the background, we could ensure that any errors during the initial sending process would trigger automatic retries later on. This approach would keep users unaffected by email delivery issues and enable them to continue using the app seamlessly.

Following devise’s guide on using ActiveJob for asynchronous email delivery, we added the following method to our User model:

def send_devise_notification(notification, *args)
  devise_mailer.send(notification, self, *args).deliver_later
end

Next, we needed to set up a backend for ActiveJob. We opted for sidekiq, a popular choice for background job processing. However, you can select any backend listed in the official Rails guides to suit your preferences. To set up sidekiq, we made the following changes:

  1. Added the sidekiq gem to our Gemfile:
gem "sidekiq"
  1. Configured ActiveJob to use the sidekiq backend in config/application.rb:
config.active_job.queue_adapter = :sidekiq
  1. Ran bundle install and restarted the server. Additionally, we started the sidekiq worker process with the necessary environment variables:
SMTP_USER="your_mailbox_user" SMTP_PASSWORD="your_pass" bundle exec sidekiq

With the setup complete, we retried the user registration process. This time, we experienced a smooth registration without any errors, as the confirmation email was sent asynchronously in the background. We could also check the sidekiq logs to observe the retries in case of errors.

As a bonus, Mailsnag also allowed us to preview the emails sent by devise in our development environment. This feature proved handy for reviewing the content and formatting of the emails.

Link to the changes

TL;DR

In this article, we demonstrated how to use Mailsnag’s Simulate Errors feature to identify and handle edge cases in your SMTP integration. By applying this approach to devise, we discovered that devise sends emails synchronously, which could lead to user frustration in the event of network issues or SMTP server outages.

To enhance devise’s reliability, we refactored it to use ActiveJob for asynchronous email delivery in the background. This approach ensured that users encountered no errors during email delivery hiccups and could seamlessly continue using the app.

By implementing these changes, you can make your application more dependable and user-friendly. If you have any questions or comments, feel free to reach out to us. We’d love to hear from you!

Until next time amigos 🙌

Related Articles

Ready to get started?

No credit card required to sign up. All paid plans come with 30 day cancellation and full refund.

Get Started for Free