6

I have a rails app in which I have a function that send out quite a few emails. I would like to do it asynchronously and I thought that the method deliver_later would do that. Currently I have some delay from when the user clicks submit until the form is submitted - which is leads to a bad user experience (It is a quite simple form). My implementation looks like this:

def create

  respond_to do |format|
    if @competition.save
      [...]
      send_notification_to_team_members
    end
end

def send_notification_to_team_members
  @team.members.each do |member|
    unless member.user.eql?(current_user)
      Mailer.deliver_new_competition_notification(member.user,   @competition).deliver_later
    end
  end
end 

Currently it takes ~ 4 seconds for the action to be finished. I have also tried:

Mailer.deliver_new_competition_notification(member.user,  @competition).deliver_later(wait: 1.minute)

Then it takes even longer - I would estimate ~1 minute.

So, am I using deliver_later wrongly, or is the method not doing what I expect. In that case, is there another method I can use to improve my performance?

cweston
  • 11,297
  • 19
  • 82
  • 107
Anders
  • 2,903
  • 7
  • 58
  • 114
  • What activejob backend are you using? – Frederick Cheung Feb 28 '15 at 08:46
  • You should send e-mail asynchronously. If you are using 4.2 you can use ActiveJob, or if not you should use such solutions as Sidekiq or Rescue. – Maxim Feb 28 '15 at 08:47
  • FYI: [Here](https://blog.engineyard.com/2014/getting-started-with-active-job) is good article about using ActiveJob for sending e-mails. – Maxim Feb 28 '15 at 08:49
  • @FrederickCheung I use `class Mailer < ActionMailer::Base`, if that's what you mean? – Anders Feb 28 '15 at 08:50
  • Regarding the comment @maxd made, the same article is also posted [here](https://quickleft.com/blog/getting-started-with-active-job/), where I posted comments on some changes I had to make to get it to work. – uhezay Jul 26 '15 at 05:48

1 Answers1

15

deliver_later uses ActiveJob to provide asynchronous execution.

However ActiveJob does not provide asynchronicity itself - it is a unifying api layer that can be fulfilled by many backends. The default one just runs everything inline it is not async.

To get async usage you need to pick an asynchronous backend. You can configure the backend in your application's config

 config.active_job.queue_adapter = ...

In general most adapters require a corresponding gem (eg delayed_job, sidekiq, sucker_punch) which may have their own dependencies too (for example sidekiq requires that you use redis.

Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174
  • Thanks! I decided to install `delayed_job`, with: `gem 'delayed_job_active_record'`, and I have followed the installation process here: https://github.com/collectiveidea/delayed_job_active_record. In `application.rb` I've added: `config.active_job.queue_adapter = :delayed_job`. And I use it like this: `Mailer.deliver_new_competition_notification(member.user, @competition).deliver_later!`. I don't get any errors, but the emails doesn't get delivered - on `localhost:3000`. Have I missed something? – Anders Feb 28 '15 at 18:37
  • Thanks, I decided to go with `sidekiq` instead. Thought it looked easier to setup. But I ran into some other issues, asked a new question here: http://stackoverflow.com/questions/28796293/rails-4-2-sidekiq-not-sending-emails-in-development – Anders Mar 01 '15 at 16:57
  • 3
    Easier than delayed_job? Worth mentioning that sideqik requires your code (and dependencies) to be thread-safe. As a happy medium I would recommend resque, as it's way faster than delayed_job, but doesn't require the thread safety of sideqik. – mpowered Mar 24 '15 at 15:33