6

I have my transactional email system setup & by default people get emails as events happen:

class Comment

  after_create :email_original_poster

  def email_original_poster
    UserMailer.delay.notify_author_of_comment self
  end

end

However instead of getting the email as-it-happens, a chunk of my users would prefer a daily or weekly digest.

What's the cleanest most elegant way to implement this?

I've already got delayed_job running but this doesn't really feel like a delayed_job job since I'm queueing data that needs to be acted on rather than actions that need to be executed.

...without reinventing a queueing system

I know that the obvious solution is table of queued_emails and of course I could do that. The reason I'm asking the question is that to do so is reinventing a queueing system. Not only are there lots of queuing systems out there but as this well worded post from Percona points out, it's a good idea not to roll your own:

http://www.engineyard.com/blog/2011/5-subtle-ways-youre-using-mysql-as-a-queue-and-why-itll-bite-you/

Have you implemented a digest email, did you use delayed_job & what did you learn?

Peter Nixey
  • 16,187
  • 14
  • 79
  • 133

3 Answers3

2

A digest looks like more appropriate to be done as a cron job like. You would still have to control to "who you have sent", to deal with crashes and errors, but the idea is to run, for example, on a daily basis, a custom rake task that craft your mail message including all info in a digest format and them send or queue for sending.

Gabriel Mazetto
  • 1,090
  • 1
  • 12
  • 23
  • I agree that this is the most sensible triggering mechanism but it still doesn't address the queueing aspect which is that you still need to queue the emails to be processed by that cron job. As you said you also need to ensure that they are processed and deal with the ones that aren't etc so the cron alone doesn't get that far. – Peter Nixey Oct 05 '11 at 12:24
  • if the queue is your concern, have you tried instead of delayed_job (relational databased based), resque (redis based)? Recently I've made some research on redis's benchmarks and it showed up as a faster monster. I really think you can't do this without some sort of queue, and resque/redis seems like to be a good shot as it's persistent queue and will not fill up your sql database. – Gabriel Mazetto Oct 06 '11 at 07:29
  • I agree. I think this is the approach that will probably be most appropriate - Nate actually suggested a plugin based on resque and designed for this task so that's the first thing I'm going to try – Peter Nixey Oct 07 '11 at 10:57
  • As 2014, I would suggest you to try sidekiq instead of resque, to have even better performance. – Gabriel Mazetto Sep 05 '14 at 17:44
1

In case anyone wants more details on a great way to do this, there's a good overview here (second answer): Sending emails based on intervals using Ruby on Rails

Community
  • 1
  • 1
Galen
  • 636
  • 6
  • 12
1

Obviously you shouldn't have to create your own queueing system. I haven't used delayed_job, but I have used resque in conjunction with a nice little gem called resque_mailer that should do exactly what you want. What's nice about resque_mailer is that, once it's set up, you don't have to change how you'd normally send mail: MyMailer.some_mailing({vars}).deliver If for some reason you want to not use the queue to send the mail (send it right away), you just need to add ! after deliver and it'll do just that.

Look into resque (with redis) and resque_mailer, I think it'll do what you want.

Nate
  • 386
  • 3
  • 4
  • 1
    Thank you for this. It's going to be a while before I'm able to go through it and figure out how it works but I'll be sure to come back to your answer once I have – Peter Nixey Oct 04 '11 at 12:50
  • Sounds great. Pretty much everything I know about Resque I learned from this Railscast: http://railscasts.com/episodes/271-resque – Nate Oct 05 '11 at 17:02