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!