Forum: Ruby on Rails Best way to do task in background in Rails

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Ed L. (Guest)
on 2006-05-01 23:24
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
François B. (Guest)
on 2006-05-03 20:59
(Received via mailing list)
Hi !

2006/5/1, Ed Lau <removed_email_address@domain.invalid>:
> <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 !
This topic is locked and can not be replied to.