Abhinav Rai

Threading in Rails

Task: Make an API which makes 10 API calls to other services. Combine their result and return.

Traditional Way: Use some HTTP client (https://github.com/nahi/httpclient for me). A for loop and combine them. It was taking about 20+ seconds for me.

Solution: Threading. Some googling and then I could come up with the code to perform the task.

def get_detailed_hashtags_in_parallel_calls(detailed_hashtags)
  batch_count = (detailed_hashtags.size / MAX_THREADS)
  final_response = []
  for i in 0..batch_count do
    threads = []
    for j in 0..(MAX_THREADS-1) do
      threads << Thread.new(j) do |j|
        unless i*MAX_THREADS + j >= detailed_hashtags.size
          response = HashtagClient.get_detail_info_for_hashtag(detailed_hashtags[i*MAX_THREADS + j])
          if response.valid?
            final_response.push(make_unique_hashtag_response(detailed_hashtags[i*MAX_THREADS + j], response.body))
          end
        end
      end
    end
    threads.each(&:join)
  end
  final_response
end

Line 1: detailed_hashtag is an array of hastags. Eg: [“nature”, “world”, “insta”, ….]

For every one of this hashtag, I have to make an API call. One way is to run each of them in a separate thread but our array size can be big and having those many threads might not be ideal.

Line 2: So we have something called MAX_THREADS.

  1. I divide the array into batches with each batch size as the size of MAX_THREAD.
  2. For each of these batches, I will run all all requests in threads and wait till every request returns a response
  3. What if error comes?
threads.each do |x|
  begin
    x.join
  rescue RuntimeError => y 
    puts "Failed:: #{y.message}" 
  end
end

Improvements: With 8 threads, the time went down to 4 sec from 30 seconds.

Further More: I didn’t know about this awesome ruby gem to do all these tasks. Parallel — https://github.com/grosser/parallel . Comment below if someone used it.

You may contact the author at abhinav.rai.1996@gmail.com