All original content is created in Ukrainian. Not all content has been translated yet. Some posts may only be available in Ukrainian.Learn more

Pessimistic Lock in Rails: what it is and when to use it. What are the alternatives?

Post cover: Pessimistic Lock in Rails: what it is and when to use it. What are the alternatives?
This content has been automatically translated from Ukrainian.
If you are working with Rails and need to avoid simultaneous modifications of a single record by multiple processes, you should pay attention to the record locking mechanism in the database. One of the most reliable ways to do this is pessimistic locking.

What is a pessimistic lock?

A pessimistic lock means that a record in the database is physically locked for changes from other threads or processes until the current operation is completed. That is, if one process has acquired a lock on a record, others must wait until it is released.
In ActiveRecord, pessimistic locking is implemented through the lock method, which adds SELECT ... FOR UPDATE, holding the lock until the end of the transaction.

Usage in Rails

Here is a simple example of using lock in Rails:
ActiveRecord::Base.transaction do
  order = Order.lock.find(order_id)
  order.status = "processed"
  order.save!
end
In this case, if another process tries to get an Order with the same order_id, it will have to wait until the current process completes the transaction.

Why is this needed?

Imagine we are processing an order. When a user clicks the "Pay" button, we need to decrease the quantity of the product in stock. If several people are trying to buy the last unit of the product at the same time, without locking, a race condition is possible:
  1. Process 1 reads the quantity of the product (1 unit).
  2. Process 2 reads the same quantity (1 unit).
  3. Both processes attempt to write a new value (0 units).
  4. One of the processes loses the update of the other.
However, if we use a lock, the second process will have to wait for the first to finish before deciding what to do.

Alternatives to pessimistic lock

Pessimistic locking is not always the best choice, as it can block other processes, slowing down the application. Sometimes it is better to use:
  1. Optimistic Locking — checks if the record has changed between reading and writing.
  2. Atomic operations — uses UPDATE ... WHERE or increment! without reading values in Ruby.
  3. Queue-based approach — distributes updates through a queue (Sidekiq, RabbitMQ).

Optimistic Locking in Rails

Optimistic locking works through the lock_version column. If two processes read a record, then when trying to write changes, the system checks if the record has changed in the meantime:
class Order < ApplicationRecord
  attr_accessor :lock_version
end
Then, if one process makes changes while another tries to write an outdated version, it will receive ActiveRecord::StaleObjectError.
In simple terms:
  • Pessimistic locking locks the record to avoid conflicts.
  • Optimistic locking allows working without locking but requires conflict handling.
  • Atomic operations reduce the risk of race conditions without locks.
Choose the right approach depending on the task. If it is critical to avoid conflicts at all costs — use a lock. If performance is important and conflicts can be tolerated — better to use lock_version or atomic updates.

This post doesn't have any additions from the author yet.

The structure of Promise (JavaScript) and how to work with it
18 Feb 14:33

The structure of Promise (JavaScript) and how to work with it

meme code
meme code@memecode
What is Memoization (examples in Ruby and Ruby on Rails)?
20 Feb 18:16

What is Memoization (examples in Ruby and Ruby on Rails)?

meme code
meme code@memecode
What is debounce in JavaScript and why is it important?
21 Mar 16:39

What is debounce in JavaScript and why is it important?

meme code
meme code@memecode
What is CFB (Cipher Feedback)?
21 Mar 16:53

What is CFB (Cipher Feedback)?

meme code
meme code@memecode
What is XOR and how does it work?
21 Mar 17:05

What is XOR and how does it work?

meme code
meme code@memecode
Embedded programming: what it is and how to get started
24 Mar 16:48

Embedded programming: what it is and how to get started

meme code
meme code@memecode
Why does PostgreSQL skip IDs when saving new records? (Heroku)
31 Mar 19:13

Why does PostgreSQL skip IDs when saving new records? (Heroku)

meme code
meme code@memecode
[Codecov] What is the difference between patch and project coverage?
09 Apr 16:03

[Codecov] What is the difference between patch and project coverage?

meme code
meme code@memecode
How do Scratch courses help children develop soft skills?
11 Apr 18:24

How do Scratch courses help children develop soft skills?

meme code
meme code@memecode
24 Apr 20:17

Fixing minikube "You are trying to run the amd64 binary on an M1 system."

meme code
meme code@memecode
24 Apr 20:55

Setting up minikube on Mac with M1 (abandoning qemu, running on docker)

meme code
meme code@memecode
Where to find an older version of Google Chrome and download it? Using an old Mac as an example.
25 Apr 23:02

Where to find an older version of Google Chrome and download it? Using an old Mac as an example.

meme code
meme code@memecode