Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use multi-threading in rails 3?

I am sending mail to the users using actionmailer through postmark. This is my code in controller:

@users = User.where(some condition)
@product = Product.find_by_name(some name).first
for user in @users
  UserMailer.new_product_arrival(user, @product, home_url).deliver
end

and this my user_mailer.rb

def new_product_arrival(user,product,home_url)
  @from         = Settings.mailer_from_address
  @recipients   = user.login
  @sent_on      = Time.now
  @user = user
  @product = product
  @content_type = "text/html"
  @home_url = home_url
end

The problem is that if there are more than 10 users it takes a very long time because of the for loop. I need to know if we can handle this by using multi-threading or background job. I don't want to use background job, but can anyone tell me how to implement the above using multi-threading.

I am using ruby 1.8.7 and rails 3.0.7

like image 690
anil.n Avatar asked Nov 22 '11 16:11

anil.n


People also ask

How do you use multithreading in Ruby?

This is the initial thread of execution that began when the Ruby program was started. You can wait for a particular thread to finish by calling that thread's Thread. join method. The calling thread will block until the given thread is finished.

Is RoR multithreaded?

It's not a common production platform among the RoR community. As a result, Eventhough Rails itself is thread-safe since version 2.2, there isn't yet a good multi-threaded server for it on Windows servers. And you get the best results by running it on *nix servers using multi-process/single-threaded concurrency model.

Is Ruby single threaded or multithreaded?

The Ruby Interpreter is single threaded, which is to say that several of its methods are not thread safe. In the Rails world, this single-thread has mostly been pushed to the server.

How many threads can Ruby handle?

The Ruby interpreter handles the management of the threads and only one or two native thread are created.


2 Answers

There basically two ways to wrap your loop in order to get "multi-threading":

  1. Spwan a thread for each delivery and join them back to the main thread

    threads = []
    for user in @users
       threads << Thread.new do
         UserMailer.new_product_arrival(user, @product, home_url).deliver
       end
    end
    threads.each(&:join)
    
  2. fork over the entire rails app ( pretty messy but the rails app serving the request will respond immediately ) and have the process detached:

    process = fork do
      for user in @users
        UserMailer.new_product_arrival(user, @product, home_url).deliver
      end
      Process.kill("HUP") 
      #sends the kill signal to current Process, which is the Rails App sending your emails 
    end
    Process.detach(process)
    

Hope that helps

like image 78
krichard Avatar answered Sep 18 '22 17:09

krichard


our developer Artem recently made a major update to the Postmark gem

which allows you to send emails easily in batches, which should allow you to send emails faster. Check it out.

like image 25
ibalosh Avatar answered Sep 17 '22 17:09

ibalosh