We all love Rails for it’s elegance and immense support of features that it provides to accomplish our tasks. Rails is feature rich and truly allows the developer to focus on the application instead repetitive tasks such as querying the database, manage routing, combining templates etc.
In the process of providing the developer with the ease of developing web applications, Rails often hides many details from developers. It does this deliberately because Rails doesn’t want to burden developers with what is going on under-the-hood while he/she building something useful. Developers can read the Rails source whenever they want, though, and most do once the application starts to get large.
Beginners and intermediate developers often find it difficult to get a sense of some important concepts in Rails, such as Rack, Rack Middleware, ActiveRecord, Asset Pipeline, thread safety etc. In this post, I will focus on thread safety in Rails.
Let’s first talk about multithreading. Mutltithreading is a very vital concept in Computer Science and we cannot underestimate importance of threads today. Threads are everywhere, used to perform very important work. According to Wikipedia:
In computer science, a thread of execution is the smallest sequence of programmed instructions that can be managed independently by an operating system scheduler. A thread is a light-weight process.
As Wikipedia said, a thread is a light-weight process. Threads that belongs to the same process share that process’s resources. That’s why it is more economical to have threads than processes in terms of memory. In web environments, threads are often used to handle some background job or a long running task asynchronously. But we have to be really careful when dealing with threads, or else we may experience unexpected results due to a concept called race conditions that occur in multithreaded systems. According to Wikipedia:
Race conditions arise in software when separate computer processes or threads of execution depend on some shared state. Operations upon shared states are critical sections that must be mutually exclusive. Failure to obey this rule opens up the possibility of corrupting the shared state.
We have reviewed some useful concepts in Computer Science, now let’s introduce thread safety in Rails. Thread safety in Rails is not new, indeed it dates back to Rails 2.3.*. Josh Peek has done incredible work to make Rails thread safe. Thread safety in Rails avoids the aforementioned race conditions.
It means that, in a multithreaded web-server environment, our code should be thread safe. If multiple threads are accessing our web application then our shared data should not be corrupted when all threads finish processing.
But Rails can’t guarantee this because developers can make mistakes that result in non-thread safe code. So, how does Rails provide us with the guarantee of thread safe code?
Rails, by default, adds a middleware called “Rack::Lock” to our middleware stack. This middleware is the second middleware in the default stack. To find out what middleware is included in a Rails app, simply type
rake middleware in your app directory.
The first middleware,
Rack::Lock guarantees that only one thread will be executing at a given time. If we remove this middleware then multiple threads can be executed at single time. MRI Ruby has a mechanism called the GIL (Global Interpreter Lock) or GVL (Global VM Lock / Giant VM Lock) as of Ruby 1.9. The GIL guarantees that only one thread will be executing at any given time and, in case of multithreading Ruby, it performs context switching. Ruby is intelligent enough to start processing another thread if some executing thread is waiting for an operation to complete.
Let’s have a practical example of Rails threadsafety in action.
Create a sample Rails app.
rails new test_app
cd into newly created Rails project and bundle to install the necessary gems.
After this, create a controller with some basic actions.
rails generate controller thread_safety index simple infinite
This will create a controller called
infinite actions. Open up
app/views/thread_safety/index.html.erb and paste in the following code:
This will create a page with two buttons on it. The purpose of two buttons is to send Ajax requests to their corresponding actions and display returned data in an alert box.
Let’s add some server-side code in
def simple sleep(1) render :text => "Welcome from simple method" end def infinite while true end end
The above code is quite simple.
simple is sleeping for 1 second and then rendering a simple
text/plain response to client. While the
infinite method is an infinite loop and will never return anything. Start a server by typing
rails s and navigate to http://localhost:3000/thread_safety/index
Click on the “Simple” button and, after one second, you will get a response in an alert box sent from the server. Now,click the “Infinite” button and wait for a response.
infinite method will never return because of a deliberate infinite loop. Press the “Simple” button again. If you expect to see same response from the server that we got earlier, then you are wrong :-).
This is thread safety. Rails guarantees our code to be thread safe by only allowing one thread to execute at a time. No other thread(s) can execute unless the executing thread finishes it’s processing. Since the
infinite thread will never finish because of an infinite loop no other threads will get a chance to process.
Even if we run our application in production environment we will get the same result. This is cool because only one thread is getting executed and we can guarantee that our Rails code is threadsafe. But there is a problem.
If you have deployed this application to a production environment using WEBrick (which you shouldn’t) then your users might experience poor performance because only one thread will be executing at any give time. If some thread takes longer than usual executing other threads will have to wait. This will annoy the users.
Our assets will not face this problem because, once the response is sent back to client, our server can serve static assets to multiple clients at once. This is due to static assets being handled by
ActionDispatch::Static which is the first middleware in the stack.
How can we process multiple requests without blocking other requests? We can simply disable thread safety by enabling
config.threadsafe! in the
production.rb file. When we enable this option, there is a massive change in our application.
Now, you can click the “Simple” button multiple times after clicking the “Infinite” button and it will return a response from the server. We have enable multithreading in our Rails App, but it’s our responsibility to now write thread safe code. For more, in-depth information on
config.threadsafe! please read this awesome article by Aaron Patterson.
We have successfully demonstrated the concept of thread safety in Rails. Let me share some good news with you. You can use a process-based web server to process multiple requests, even with the
config.threadsafe! option disabled. If we run our app on servers such as Unicorn or Apache Passenger / Nginx Passenger, simple Ajax requests will always get returned successfully even if we have triggered infinite Ajax request.
This is because a process-based web server creates worker processes and each process holds one instance of our application. Apache Passenger spawns a child process for each request it handles. So, when we issue the infinite Ajax request, one worker thread starts handling that request and when we instantiate simple Ajax request it gets handled by a newly spawned child. Aaron Patterson mentions in his article that
config.threadsafe! has no effect in process-based servers.
I hope you learned something about Rails and thread safety today. Thanks for reading!