Optimistic Locking vs Pessimistic Locking in Ruby on Rails

Locking in a database means that no two or more sessions can update the same record at the same time until the lock is released. In this article we will discuss two main types of locking in Ruby on Rails; Optimistic Locking and Pessimistic Locking.

Optimistic locking assumes that a database conflict will happen less often. It takes care of locking if the lock_version field is specified in the table. Here’s how to implement it:

1
2
rails new locking
rails generate model Notification body:text

Add lock_version in the migration file.

1
2
3
4
5
6
7
8
9
10
class CreateNotifications < ActiveRecord::Migration[6.0]
 def change
   create_table :notifications do |t|
     t.text :body
     t.integer :lock_version, default: 0

     t.timestamps
   end
 end
end

Migrate the database.

1
rails db:migrate

Now test the implementation.

1
2
3
4
5
6
7
8
Notification.create(body: "aaaaaaaa")
n1 = Notification.first
n2 = Notification.first
n1.body = "n1"
n1.save
n2.body = "n2"
n2.save
ActiveRecord::StaleObjectError (Attempted to update a stale object: Notification.)

As you see, StaleObjectError is thrown by ActiveRecord because the record was accessed by n1 object.

Pessimistic locking, in contrast, assumes that the database conflicts happen more often. This does not need the kind of configuration required by optimistic locking, it just takes care of a record if lock or with_lock is called on the object.

1
n1 = Notification.lock.first

The above expression will lock the first notification object. It can also be implemented for a whole transaction.

1
2
3
4
5
n1 = Notification.first
n1.with_lock
  n1.body = “n1”
  n1.save
end

That is how optimistic and pessimistic locking are implemented. Happy locking!