What is config.threadsafe!

Share this article

ct

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, ActionDispatch::Static, is used to serve static assets such as JavaScript, CSS files and images.

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

Now cd into newly created Rails project and bundle to install the necessary gems.

bundle install

After this, create a controller with some basic actions.

rails generate controller thread_safety index simple infinite

This will create a controller called ThreadSafetyController with index, simple and infinite actions. Open up app/views/thread_safety/index.html.erb and paste in the following code:

<button id="simple_request">Simple Request</button>
&nbsp;
<button id="infinite_request">Infinite Request</button>

<script type="text/javascript">
    $("#simple_request").click(function() {
        $.get("/users/simple", function(data) {
            alert(data)
        });
    });

    $("#infinite_request").click(function() {
        $.get("/users/infinite", function(data) {
            alert(data)
        });
    });
</script>

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 app/controllers/thread_safety_controller.rb

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.

The 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 development.rb or 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!

Frequently Asked Questions (FAQs) about Thread Safety in Ruby

What is the concept of thread safety in Ruby?

Thread safety in Ruby refers to the ability of code to function correctly during simultaneous execution by multiple threads. In a thread-safe environment, shared data structures will not be manipulated in a way that can corrupt the data or lead to unpredictable results. This is crucial in multi-threaded applications where different threads can potentially access and modify shared data at the same time.

How can I ensure thread safety in my Ruby application?

Ensuring thread safety in Ruby involves several practices. First, avoid sharing mutable objects between threads. If sharing is unavoidable, use synchronization techniques like Mutexes to prevent concurrent modification. Second, use thread-safe libraries when possible. Lastly, be aware of Ruby’s Global Interpreter Lock (GIL), which can help ensure thread safety but may also limit parallelism.

What is the Global Interpreter Lock (GIL) in Ruby?

The Global Interpreter Lock (GIL) is a mechanism used in Ruby to synchronize the execution of threads. It ensures that only one thread can execute Ruby code at a time, even on a multi-core processor. This helps to prevent race conditions and other threading issues, but it can also limit the degree of parallelism that can be achieved.

What are the potential issues with thread safety in Ruby?

The main issues with thread safety in Ruby revolve around data corruption and unpredictable behavior due to race conditions. A race condition occurs when the behavior of a program depends on the relative timing of threads. If not properly managed, this can lead to inconsistent or incorrect results.

How does Ruby handle thread safety in its standard library?

Ruby’s standard library is largely thread-safe, with some exceptions. Certain classes, like Array and Hash, are not thread-safe and can lead to issues if shared between threads without proper synchronization. It’s always a good practice to check the documentation of each class or module to understand its thread safety guarantees.

What is a Mutex and how does it help in ensuring thread safety?

A Mutex, or mutual exclusion object, is a synchronization primitive that can be used to protect shared data from concurrent access. By locking a Mutex before accessing shared data and unlocking it afterwards, you can ensure that only one thread can access the data at a time, preventing race conditions.

Are all Ruby libraries thread-safe?

Not all Ruby libraries are thread-safe. It’s important to check the documentation of each library to understand its thread safety guarantees. If a library is not thread-safe, you may need to use synchronization techniques or other strategies to prevent threading issues.

How does thread safety affect performance in Ruby?

Thread safety can both positively and negatively affect performance. On one hand, multi-threading can improve performance by allowing tasks to be performed concurrently. On the other hand, synchronization techniques like Mutexes can introduce overhead and limit parallelism, potentially reducing performance.

Can I use Ruby’s Thread class to create thread-safe code?

Yes, Ruby’s Thread class can be used to create multi-threaded applications. However, simply using Threads does not guarantee thread safety. You must still ensure that shared data is properly synchronized to prevent race conditions and other threading issues.

What are some best practices for writing thread-safe code in Ruby?

Some best practices for writing thread-safe code in Ruby include avoiding shared mutable state, using thread-safe libraries, understanding the implications of the Global Interpreter Lock (GIL), and using synchronization techniques like Mutexes when necessary. It’s also important to thoroughly test your code to ensure it behaves correctly under concurrent execution.

Imran LatifImran Latif
View Author

Imran Latif is a Web Developer and Ruby on Rails and JavaScript enthusiast from Pakistan. He is a passionate programmer and always keeps on learning new tools and technologies. During his free time he watches tech videos and reads tech articles to increase his knowledge.

rails
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week