We all like Rails for its elegance and the range of features it supports, quite apart from the fact that it keeps pace with the technology of the day, innovates constantly and keeps opening up new possibilities. Rails lets a developer concentrate on the actual work instead of wrestling with the language itself and how to configure this or that. It favours convention over configuration.
For some people, though, that can be a complication, because the Rails framework hides most of its configuration away inside itself and treats it as the default. A developer who wants to make bigger changes to those settings has to know how, or where to look it up. Developers starting out usually struggle to understand Rails concepts such as Rack, Rack Middleware, ActiveRecord, the Asset Pipeline or thread safety. This article is about thread safety in particular.
What multithreading actually is
Multithreading is a very important concept in computer science and it deserves more attention than it gets. It means processing a given piece of program code or set of instructions in several threads independently of each other, with requests to the processor running in parallel. You’ll find it used, for example, when handling long tasks that run asynchronously in the background.
So how does it work in Ruby on Rails? Thread safety was introduced in Rails as far back as version 2.3. It means that if we have a web server that supports multiple threads, our code should be thread safe. If several threads access our application at once, our shared data shouldn’t end up corrupted once all the threads have finished.
So how does Rails guarantee that our application is thread safe?
By default Rails adds a middleware called “Rack::Lock”. It sits second in the default middleware stack. If you want to list all the middleware your application uses, just run rake middleware in the root of your application.
The first middleware, ActionDispatch::Static, is used to serve static assets such as JavaScript, CSS and images.
Rack::Lock guarantees that only one thread runs at any given moment. If we remove this middleware, several threads will run at the same 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 also guarantees that only one thread runs at a time, but it adds context switching on top of that. Ruby is smart enough to start a process while another process is waiting on some operation to complete.
Let’s look at an example of thread safety in a Rails application.
We’ll create a Rails application.
rails new test_app
Now let’s move into the project folder and run bundle to install the necessary gems.
bundle
Then we’ll generate a controller with a couple of actions.
rails generate controller thread_safety index simple infinite
This command generates a ThreadSafetyController with the actions index, simple and infinite. Open the template at app/views/thread_safety/index.html.erb and paste in the following code:
That gives us a page with two buttons whose job is to send Ajax requests to our controller actions and show the data in an alert box.
Now let’s add a little code to our controller at app/controllers/thread_safety_controller.rb
def simple sleep(1) render :text => "Welcome from simple method" end def infinite while true end end
The code above is very simple. The simple method sleeps for one second and then returns plain text to the client, whereas the infinite method has an infinite loop and never returns anything, because it runs forever. Start the server with rails s and go to http://localhost:3000/thread_safety/index
Click the “Simple Request” button and after a second you’ll get a response from the server in an alert box. Now click “Infinite Request” and wait for the response.
The infinite method never returns a result, because of the infinite loop. Press “Simple Request” again. If you’re expecting a response from the server like the one a moment ago, you’d be wrong :-).
This is thread safety. Rails guarantees that our application is safe in this respect by running only one request at a time. It won’t start any other threads until the process already running has finished.
If we run the application in production mode, we get the same result. That’s good and correct behaviour, because Rails is keeping our code safe.
But there’s a problem
If you deployed this application to a production server using the WEBrick web server (which you shouldn’t), your users could run into trouble, because only one request would be served in one thread at a time. If one of the threads takes a while, the others end up waiting. That’s the kind of behaviour that irritates users.
Our assets will be served by the server straight away, because this setting doesn’t apply to them. Assets are handled by the ActionDispatch::Static middleware, so a given asset can be served to several users at the same time.
So how can we handle requests without blocking the others? We can simply turn on config.threadsafe! in the development.rb or production.rb file. Enabling that option makes a massive difference to our application.
Now you can click the “Simple” button several times after pressing “Infinite” and the server will return results. We’ve turned on multithreading support — but it’s now our responsibility to write safe code.
In this article we’ve walked through the concept of thread safety in Ruby on Rails. In real applications, though, things work differently. You can use process-based web servers to serve multiple requests even with config.threadsafe! switched off — Unicorn and Passenger, for instance. That’s because process-based web servers create workers, each of which holds an instance of your application, so the server can handle several requests at once.