Practical Rails Lesson: Threading with Apache (DRb Part I)
If your app has to do some heavy processing, you will likely encounter the ‘rails application failed to start’ error, because Apache (for one reason or another) just gives up. If you watch your CPU meter when this occurs, you can see that the process you started is still going, even though Apache has decided to error out. One solution to this problem is to use threading.
If you take a look at the Running Background Jobs in Ruby page, you might start to get excited about using threads. Forking a task to the background looks like a perfect solution, until you read further down the page, where it says (with no explanation whatsoever) that it won’t work with Apache. In the ever-so-useful #rubyonrails (irc.freenode.net), I asked if there were any workarounds, and was eventually pointed towards Distributed Ruby, or DRb.
The docs concerning Distributed Ruby are, well, very weak. The wiki means well, but doesn’t even approach useability or an explanation. This piece from Rubycentral actually had some useable information, and served as my starting place.
First, assume you want to do something trivial in your thread, like count to a million. Also assume that Apache can or will timeout during this processing. The next step is to figure out how to start a DRb server, then how to use it with your rails app. Since pretty much everyone’s implementation will differ, I’m providing just enough information to give you a jump start. A few things to note before we really get started: The DRb server will need to be running standalone. I usually leave a terminal window open so I can monitor what it is doing. My example works for my application, but I have no doubt that we will find better/faster ways to implement this. If you find a better way, please let me know!
Elements of a DRb Server:
There are two important questions to ask when you build your DRb server.
- Will it need access to your models/database?
- Will the page be showing/updating the status of the background process?
If the answer to either of these is yes, then your DRb startup script will need to include some of your configuration from the Rails app. If not, then your life will be much easier, and you can just start a server and use it to run commands.
Example 1: Simple DRb without model access
In your RailsApp/script/ folder, create a file called startDRb.rb with the following contents [Download Code]:
puts “Starting Distributed Ruby Server.”
require ‘drb’
require ‘Myserver’
serverClass = Myserver.new
DRb.start_service(’druby://127.0.0.1:9000′, serverClass)
puts “Distributed Pipertable Server Started”
DRb.thread.join
The next thing we will need to create is the Myserver.rb class [Download code]:
class Myserver
def countHigh
puts(”Starting Big Process”)
Thread.new do
keepgoing=true
x=0
while keepgoing
keepgoing=false if x == 10000000
x += 1
end
system(”touch /tmp/DONE.txt”)
end
end
end
If you want to put your Myserver.rb in your site’s lib folder, you can. If you do so, the “require ‘Myserver’” line will need to be changed to:
require File.dirname(__FILE__) + ‘/../lib/Myserver’
Now, build an action in one of your controllers, and do the following [Download Code]:
def someActionThatTakesForever
myServer = DRbObject.new(nil, ‘druby://127.0.0.1:9000′)
myServer.countHigh()
redirect_to :action => “someOtherAction”
end
With your DRb server running, when you navigate to http://whatever/controller/someActionThatTakesForever/ you should immediately be taken to someOtherAction while the countHigh() task runs. If you watch your /tmp folder and the terminal, you should see the task execute and when it finishes you will see a new .txt file.
Check back for part II!