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!