Hey everyone, I'm still getting up to speed with Rails and Ruby. Is there a best practice technique for kicking off a thread to work on an activity in the background? Here's the scenario: the user performs an action that triggers e-mails to be sent. (I'm using ActionMailer). My app acknowledges with a message to the user that the messages have been sent. I would like to show the acknowledgment right away instead of the noticable delay I'm seeing now depending on the number of recipients. In my ActionController I do this: <code> def send_invites ... thread = Thread.new(e) do |event| EventMailer.send_invites(event) end thread.join ### this is what I'm curious about! ... end </code> I've tested my code with Webrick and lighttpd on the Mac (Rails 1.1). I've found that without the thread.join, the code doesn't work--the EventMailer doesn't get to do its work. From what I've read in the pickaxe book, I think it's because the spawned thread is killed when the invoking (Rails) thread exits. The code works with the join, but this defeats the purpose of what I'm trying to do as the UI thread blocks. Am I taking the right approach here? Will my results be different under Apache/FastCGI? I came across DRb and Rinda when I did a quick search. Because I don't need to communicate with the background task, I'd prefer a really simple approach before I investigate DRb, Rinda or something else. Thanks in advance for advice and feedback! --Ed Lau
on 2006-05-01 21:24
on 2006-05-03 18:59
Hi ! 2006/5/1, Ed Lau <firstname.lastname@example.org>: > <code> > def send_invites > ... > thread = Thread.new(e) do |event| > EventMailer.send_invites(event) > end > thread.join ### this is what I'm curious about! > ... > end > </code> Look into rails_cron or plain cron. In your controller, mark the event as ready to send, and then use a class method in Event to do the send: class Event def sent? self.sent_at end def send! return if self.sent? self.class.transaction do EventMailer.send_invites(self) self.sent_at = Time.now self.save! end end def self.send_ready self.find_ready.send! end end You can then run this code using script/runner: crontab -l * * * * * /u/me/apps/rails-app/script/runner -e production "Event.send_ready" Using rails_cron: class Event background :send_ready, :every => 1.minute end Hope that helps !