Thread-Safety in Ruby on Rails: Basic Example With Race Conditions
22 October 2024 · 2 min read · Rubycode
The Global Interpreter Lock (GIL) in Ruby prevents true multi-core CPU usage in a Rails app, but there is still concurrency, especially when using a multi-threaded server like Puma. While Ruby cannot execute multiple threads in parallel due to the GIL, Puma can handle multiple requests concurrently. This means that when one thread is waiting for a database transaction or an external service, other threads can continue working.
Imagine we have a simple controller that increments the page view counter. If 100 users hit the same page within one second, we could face a problem. Without using atomic operations or a Mutex, some of those page views could be lost, and the counter might not reflect the correct total. This happens due to a race condition, where multiple threads are trying to update the same resource at the same time.
BAD PRACTICE
class PageViewsController < ApplicationController
def show
@page_view = PageView.find(params[:id])
@page_view.view_count += 1
@page_view.save
render json: @page_view
end
end
If multiple users request this page at the same time, say 5 users hit the show action concurrently, the following could happen:
Each user (request) fetches the same PageView record from the database.
Each request reads the current view_count (e.g., 100).
Each request increments the view_count to 101.
Each request saves view_count as 101 in the database.
Even though 5 requests were made, the view_count was only incremented by 1 instead of 5 because the changes made by one request are overwritten by others. This is a classic race condition due to concurrent database access in a multi-threaded environment (handled by Puma)
GOOD PRACTICE
class PageViewsController < ApplicationController
def show
@page_view = PageView.find(params[:id])
@page_view.increment!(:view_count)
render json: @page_view
end
end
The increment! method generates an SQL query that increments the view_count directly in the database in an atomic way. Even if multiple requests hit the server at the same time, the database ensures that each increment operation is applied correctly, preventing lost updates
Need engineers who write code like this?
We place vetted developers, designers, QA and delivery specialists with enterprises and startups across the EU. Tell us what you need and we'll shortlist people who fit.
Book a 30-minute callMore from the blog
-
02 October 2026
Where Is the Engineering Work Now? Redesigning Development Around AI
Agents already write most of the code in a sprint, yet we still run it as if every developer types their ow...
-
30 September 2026
The agents write the code. Your team decides what gets built.
plan_driven is an open source Ruby gem that takes a Rails feature from an approved implementation plan to m...
-
29 September 2026
You don't need a heavy agent to count your customers
rails_agent_console puts a small AI inside rails console. Ask in English, read the ActiveRecord query befor...